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

Related Posts:

  • jQuery Tutorial jQuery tutorial for beginners and professionals provides deep knowledge of jQuery technology. Our jQuery tutorial will help you to learn jQuery … Read More
  • Admin Session Lifetime | Adobe Commerce 2.4This article shows how you can increase the admin login session lifetime in Magento 2.ProblemWhen you are a developer, it is frustrating to login to a… Read More
  • DBMS Tutorial DBMS TutorialDBMS Tutorial provides basic and advanced concepts of Database. Our DBMS Tutorial is designed for beginners and professionals … Read More
  • JavaScript Tutorial Learn JavaScript TutorialOur JavaScript Tutorial is designed for beginners and professionals both. JavaScript is used to create client… Read More
  • Laravel package for the Google Translate API Laravel Google Translate is a package that provides an artisan console command to translate your localization files with the Google translation … Read More
Newer Post Older Post Home

0 comments:

Post a Comment

Thanks

Meta

Popular Posts

  • Vue3 :style backgroundImage not working with require
    I'm trying to migrate a Vue 2 project to Vue 3. In Vue 2 I used v-bind style as follow: In Vue 3 this doesn't work... I tried a...
  • SQL ORDER BY Keyword
      The SQL ORDER BY Keyword The ORDER BY keyword is used to sort the result-set in ascending or descending order. The ORDER BY keyword sorts ...
  • Enabling authentication in swagger
    I created a asp.net core empty project running on .net6. I am coming across an issue when I am trying to enable authentication in swagger. S...
  • failed to load storage framework cache laravel excel
       User the export file and controller function  ..         libxml_use_internal_errors ( true ); ..Good To Go   public function view () : ...
  • Features CodeIgniter
    Features CodeIgniter There is a great demand for the CodeIgniter framework in PHP developers because of its features and multiple advan...

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)

  • Failed to install 'cordova-plugin-firebase': CordovaError: Uh oh - 9/21/2024
  • pyspark XPath Query Returns Lists Omitting Missing Values Instead of Including None - 9/20/2024
  • SQL REPL from within Python/Sqlalchemy/Psychopg2 - 9/20/2024
  • MySql Explain with Tobias Petry - 9/20/2024
  • How to combine information from different devices into one common abstract virtual disk? [closed] - 9/20/2024

Laravel News

  • Cast Model Properties to a Uri Instance in 12.17 - 6/4/2025
  • Simplify Negative Relation Queries with Laravel's whereDoesntHaveRelation Methods - 5/31/2025
  • Efficiently remove expired cache data with Laravel Cache Evict - 6/3/2025
  • Test Job Failures Precisely with Laravel's assertFailedWith Method - 5/31/2025
  • Prism Relay - 6/2/2025

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