CoderFunda
  • Home
  • About us
    • Contact Us
    • Disclaimer
    • Privacy Policy
    • About us
  • Home
  • Php
  • HTML
  • CSS
  • JavaScript
    • JavaScript
    • Jquery
    • JqueryUI
    • Stock
  • SQL
  • Vue.Js
  • Python
  • Wordpress
  • C++
    • C++
    • C
  • Laravel
    • Laravel
      • Overview
      • Namespaces
      • Middleware
      • Routing
      • Configuration
      • Application Structure
      • Installation
    • Overview
  • DBMS
    • DBMS
      • PL/SQL
      • SQLite
      • MongoDB
      • Cassandra
      • MySQL
      • Oracle
      • CouchDB
      • Neo4j
      • DB2
      • Quiz
    • Overview
  • Entertainment
    • TV Series Update
    • Movie Review
    • Movie Review
  • More
    • Vue. Js
    • Php Question
    • Php Interview Question
    • Laravel Interview Question
    • SQL Interview Question
    • IAS Interview Question
    • PCS Interview Question
    • Technology
    • Other

01 January, 2024

Calling different functions of a DLL from different threads simultaneously segfaults or gives a status_stack_buffer_overrun

 Programing Coderfunda     January 01, 2024     No comments   

I'm writing a library regarding Bridge (the card game), which performs deals generation, performance analysis and other stuff. I'm using Rust and, for some functionalities, I'm leaning on the DLL of a C++ library, called Double Dummy Solver (henceforth DDS - Site, GitHub). I used bindgen to create the bindings for the DLL (I don't know if this is the correct approach: I am not a programmer by trade and I kinda had an hard time wrapping my head around how DLL and FFI works and how to use them in Rust. I'll happily accept advices regarding this.) and everything seemed to work perfectly.


After implementing a parallel function to analyse multiple deals simultaneously (AnalyseAllPlaysBin, link to the docs) and writing a little test for it, cargo test started failing with:


exit code: 0xc0000409, STATUS_STACK_BUFFER_OVERRUN
or with:
exit code: 0xc0000005, STATUS_ACCESS_VIOLATION
note: test exited abnormally; to see the full output pass --nocapture to the harness.
Segmentation fault



I was a bit surprised and I dug a bit to understand the problem. I found out that the reason for the failure was cargo running the tests in parallel, and I think simultaneous calls mess up with the DLL, making it segfault.


Using cargo t -- --test-threads=1 solves the problem, but since I'm writing a library I hope to use for an application in the future, I would like to understand the problem and solve it in some way. I'm not planning to make multiple simultaneous calls to the DLL but the problem still bothers me.

I cannot provide an MWE but I'll give some code for reference:

use dds::{
deal, // The `deal` type the C++ library uses
solvedPlays, // Type of the C++ library
solvedPlay, // Same as above
playTraceBin, // Same as above
playTracesBin, // Same as above
PlayTraceBin, // Cards played for the deal to analyze
PlayTracesBin, // Collection of PlayTraceBin for different deals
SolvedPlays, // My wrapper around the C++ type
SolvedPlay, // Same
RankSeq, // Sequence of the rank (2,3,Q,K,A ecc.) of cards played
RawDDSRef, // Trait (fn get_raw(&self)) for getting a ref to the underlying struct of the Rust wrapper types
RawDDSRefMut, // Same as above, but mut (fn get_raw_mut(&mut self))
AsRawDDS, // Same as above, but not a ref (fn as_raw(self))
SuitSeq, // Sequence of suits of cards played
Target,Mode,Solutions // Parameters used by the DLL
MAXNOOFBOARDS,
};

const TRIES: usize = 200;
const CHUNK_SIZE: i32 = 10;

pub fn initialize_test() -> DealMock {
DealMock {
hands: [
[8712, 256114688, 2199023255552, 2344123606046343168],
[22528, 10485760, 79182017069056, 744219838422974464],
[484, 1612185600, 1924145348608, 4611686018427387904],
[1040, 268435456, 57415122812928, 1522216674051227648],
],
}
}

pub trait PlayAnalyzer {
/// Analyzes a single hand
/// # Errors
/// Will return an Error when DDS fails in some way.
fn analyze_play(
deal: &D,
contract: &C,
play: PlayTraceBin,
) -> Result;
/// Analyzes a bunch of hands in paraller.
/// # Errors
/// Will return an Error when DDS fails in some way or the deals and contracts vecs have
/// different length or their length doe
fn analyze_all_plays(
deals: Vec,
contracts: Vec,
plays: &mut PlayTracesBin,
) -> Result;
}

impl PlayAnalyzer for DDSPlayAnalyzer {
#[inline]
fn analyze_all_plays(
deals: Vec,
contracts: Vec,
plays: &mut PlayTracesBin,
) -> Result {
let deals_len = i32::try_from(deals.len().clamp(0, MAXNOOFBOARDS)).unwrap();
let contracts_len = i32::try_from(contracts.len().clamp(0, MAXNOOFBOARDS)).unwrap();

if deals_len != contracts_len || deals_len == 0 || contracts_len == 0 {
return Err(RETURN_UNKNOWN_FAULT.into()); // The error tells that
// either something went terribly wrong or we used wrongly sized inputs.
}

let mut c_deals: Vec = contracts
.into_iter()
.zip(deals)
.map(|(contract, deal)| construct_dds_deal(contract, deal))
.collect();
c_deals.resize(
MAXNOOFBOARDS,
deal {
trump: -1,
first: -1,
currentTrickSuit: [-1i32; 3],
currentTrickRank: [-1i32; 3],
remainCards: [[0u32; 4]; 4],
},
);
let mut boards = boards {
noOfBoards: deals_len,
// We know vec has the right length
deals: match c_deals.try_into().unwrap(),
target: [Target::MaxTricks.into(); MAXNOOFBOARDS],
solutions: [Solutions::Best.into(); MAXNOOFBOARDS],
mode: [Mode::Auto.into(); MAXNOOFBOARDS],
};
let mut solved_plays = SolvedPlays {
solved_plays: solvedPlays {
noOfBoards: deals_len,
solved: [solvedPlay::new(); MAXNOOFBOARDS],
},
};

let bop: *mut boards = &mut boards;
let solved: *mut solvedPlays = solved_plays.get_raw_mut();
let play_trace: *mut playTracesBin = (*plays).get_raw_mut();

// SAFETY: calling C
let result = unsafe { AnalyseAllPlaysBin(bop, play_trace, solved, CHUNK_SIZE) };
match result {
// RETURN_NO_FAULT == 1i32
1i32 => Ok(solved_plays),
n => Err(n.into()),
}
}

#[inline]
fn analyze_play(
deal: &D,
contract: &C,
play: PlayTraceBin,
) -> Result {
let c_deal = construct_dds_deal(contract, deal);
let mut solved_play = SolvedPlay::new();
let solved: *mut solvedPlay = &mut solved_play.solved_play;
let play_trace = play.as_raw();
// SAFETY: calling an external C function
let result = unsafe { AnalysePlayBin(c_deal, play_trace, solved, 0) };
match result {
1i32 => Ok(solved_play),
n => Err(n.into()),
}
}
}

/// Constructs a DDS deal from a DDS contract and a DDS deal representation
fn construct_dds_deal(contract: &C, deal: &D) -> deal {
let (trump, first) = contract.as_dds_contract();
deal {
trump,
first,
currentTrickSuit: [0i32; 3],
currentTrickRank: [0i32; 3],
remainCards: deal.as_dds_deal().as_slice(),
}
}

#[test]
fn analyse_play_test() {
let deal = initialize_test();
let contract = ContractMock {};
let suitseq = SuitSeq::try_from([0i32, 0i32, 0i32, 0i32]).unwrap();
let rankseq = RankSeq::try_from([4i32, 3i32, 12i32, 2i32]).unwrap();
let play = PlayTraceBin::new(suitseq, rankseq);
let solvedplay = DDSPlayAnalyzer::analyze_play(&deal, &contract, play).unwrap();
assert_eq!([2, 2, 2, 2, 2], solvedplay.solved_play.tricks[..5]);
}

#[test]
fn analyse_all_play_test() {
let mut deals_owner = Vec::with_capacity(TRIES);
deals_owner.resize_with(TRIES, initialize_test);
let deals = deals_owner.iter().collect();
let suitseq = SuitSeq::try_from([0, 0, 0, 0]).unwrap();
let rankseq = RankSeq::try_from([4, 3, 12, 2]).unwrap();
let mut suitseqs = Vec::with_capacity(TRIES);
let mut rankseqs = Vec::with_capacity(TRIES);
suitseqs.resize_with(TRIES, || suitseq.clone());
rankseqs.resize_with(TRIES, || rankseq.clone());
let contracts_owner = Vec::from([ContractMock {}; TRIES]);
let contracts = contracts_owner.iter().collect();
let mut plays = PlayTracesBin::from_sequences(suitseqs, rankseqs).unwrap();
let solved_plays = DDSPlayAnalyzer::analyze_all_plays(deals, contracts, &mut plays).unwrap();
let real_plays = solved_plays.get_raw();
assert_eq!(TRIES, real_plays.noOfBoards.try_into().unwrap());
for plays in real_plays.solved {
assert_eq!([2, 2, 2, 2, 2], plays.tricks[..5]);
}
}




I could wrap the DDSAnalyzer in an Arc and then lock it for the duration of the external function call. I think this should work (didn't have time to try it) but I don't know if it is the correct approach.


I would like to ask two things:



* Why I get those errors in a multithreaded situation?

* Would using a Arc work? Is it the correct solution or should I do something different?






Thanks to everyone!
  • Share This:  
  •  Facebook
  •  Twitter
  •  Google+
  •  Stumble
  •  Digg
Email ThisBlogThis!Share to XShare to Facebook
Newer Post Older Post Home

0 comments:

Post a Comment

Thanks

Meta

Popular Posts

  • Write API Integrations in Laravel and PHP Projects with Saloon
    Write API Integrations in Laravel and PHP Projects with Saloon Saloon  is a Laravel/PHP package that allows you to write your API integratio...
  • Credit card validation in laravel
      Validation rules for credit card using laravel-validation-rules/credit-card package in laravel Install package laravel-validation-rules/cr...
  • iOS 17 Force Screen Rotation not working on iPAD only
    I have followed all the links on Google and StackOverFlow, unfortunately, I could not find any reliable solution Specifically for iPad devic...
  • C++ in Hindi Introduction
    C ++ का परिचय C ++ एक ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग लैंग्वेज है। C ++ को Bjarne Stroustrup द्वारा विकसित किया गया था। C ++ में आने से पह...
  • Send message via CANBus
    After some years developing for mobile devices, I've started developing for embedded devices, and I'm finding a new problem now. Th...

Categories

  • Ajax (26)
  • Bootstrap (30)
  • DBMS (42)
  • HTML (12)
  • HTML5 (45)
  • JavaScript (10)
  • Jquery (34)
  • Jquery UI (2)
  • JqueryUI (32)
  • Laravel (1017)
  • Laravel Tutorials (23)
  • Laravel-Question (6)
  • Magento (9)
  • Magento 2 (95)
  • MariaDB (1)
  • MySql Tutorial (2)
  • PHP-Interview-Questions (3)
  • Php Question (13)
  • Python (36)
  • RDBMS (13)
  • SQL Tutorial (79)
  • Vue.js Tutorial (68)
  • Wordpress (150)
  • Wordpress Theme (3)
  • codeigniter (108)
  • oops (4)
  • php (853)

Social Media Links

  • Follow on Twitter
  • Like on Facebook
  • Subscribe on Youtube
  • Follow on Instagram

Pages

  • Home
  • Contact Us
  • Privacy Policy
  • About us

Blog Archive

  • September (100)
  • August (50)
  • July (56)
  • June (46)
  • May (59)
  • April (50)
  • March (60)
  • February (42)
  • January (53)
  • December (58)
  • November (61)
  • October (39)
  • September (36)
  • August (36)
  • July (34)
  • June (34)
  • May (36)
  • April (29)
  • March (82)
  • February (1)
  • January (8)
  • December (14)
  • November (41)
  • October (13)
  • September (5)
  • August (48)
  • July (9)
  • June (6)
  • May (119)
  • April (259)
  • March (122)
  • February (368)
  • January (33)
  • October (2)
  • July (11)
  • June (29)
  • May (25)
  • April (168)
  • March (93)
  • February (60)
  • January (28)
  • December (195)
  • November (24)
  • October (40)
  • September (55)
  • August (6)
  • July (48)
  • May (2)
  • January (2)
  • July (6)
  • June (6)
  • February (17)
  • January (69)
  • December (122)
  • November (56)
  • October (92)
  • September (76)
  • August (6)

Loading...

Laravel News

Loading...

Copyright © CoderFunda | Powered by Blogger
Design by Coderfunda | Blogger Theme by Coderfunda | Distributed By Coderfunda