質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

1回答

1772閲覧

Rustでdllを作ってほかのクレートから使ったら1回の呼び出しはうまくいくが2回呼び出すと正常に動作しない

Yhaya

総合スコア439

DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

0クリップ

投稿2021/08/07 11:27

編集2021/08/07 11:32

環境

  • Windows 10
  • Rust 1.50.0

生じている問題

RustでDLLを作ってそれをほかのクレートから呼び出そうとしました。DLLを作る側は

lib.rs

rust

1use std::os::raw::{c_int, c_short, c_uchar, c_uint}; 2 3#[no_mangle] 4pub extern "C" fn TUSB0216AD_Device_Open(_id: c_short) -> c_short { 5 return 0; 6} 7 8#[no_mangle] 9pub extern "C" fn TUSB0216AD_Device_Close(_id: c_short) {} 10 11#[no_mangle] 12pub extern "C" fn TUSB0216AD_Start( 13 _id: c_short, 14 _ch: c_uchar, 15 _pre_len: c_int, 16 _trig_type: c_uchar, 17 _trig_ch: c_uchar, 18) -> c_short { 19 return 0; 20} 21 22#[no_mangle] 23pub extern "C" fn TUSB0216AD_Stop(_id: c_short) -> c_short { 24 return 0; 25} 26 27#[no_mangle] 28pub extern "C" fn TUSB0216AD_Ad_Data( 29 _id: c_short, 30 _ch: c_short, 31 _data: *mut c_int, 32 _datalen: *mut c_uint, 33) -> c_short { 34 unsafe { 35 *_data = 10; 36 *_datalen = 1; 37 } 38 39 return 0; 40} 41 42#[no_mangle] 43pub extern "C" fn TUSB0216AD_AdClk_Set(_id: c_short, _clk_time: c_int, _sel: c_uchar) -> c_short { 44 return 0; 45} 46 47#[no_mangle] 48pub extern "C" fn TUSB0216AD_Input_Set(_id: c_short, _type1: c_uchar, _type2: c_uchar) -> c_short { 49 return 0; 50} 51 52#[no_mangle] 53pub extern "C" fn TUSB0216AD_Trigger(_id: c_short) -> c_short { 54 return 0; 55} 56

生ポインタを使っていたりするのは、このDLLがもともとある装置のCインターフェースをモックするという目的で作っているためです。

これを使っている側は

lib.rs

rust

1mod helper; 2 3use std::sync::{Arc, Mutex}; 4use std::thread; 5 6#[no_mangle] 7pub extern "C" fn run(id: i32, seconds: u64) { 8 // sequence が走っているかを示すフラグ 9 // -1: not-started, 0: running, 1: finished 10 let flag = Arc::new(Mutex::new(0)); 11 const DATA_SIZE: usize = 50000; 12 13 let flg1 = Arc::clone(&flag); 14 let time_keeper = thread::spawn(move || { 15 helper::continuous_read(id, seconds, flg1); 16 }); 17 18 let flg2 = Arc::clone(&flag); 19 let mut x = vec![0.0 as f32; DATA_SIZE]; 20 let mut y = vec![0.0 as f32; DATA_SIZE]; 21 22 let job_runner = thread::spawn(move || { 23 helper::get_data(id, flg2, &mut x, &mut y); 24 }); 25 26 time_keeper.join().unwrap(); 27 job_runner.join().unwrap(); 28}

helper.rs

rust

1use std::sync::{Arc, Mutex}; 2use std::{thread, time}; 3 4#[link(name = "ad_mock.dll", kind = "dylib")] 5#[allow(dead_code)] 6extern "C" { 7 pub fn TUSB0216AD_Device_Open(id: i32) -> i32; 8 pub fn TUSB0216AD_Device_Close(id: i32); 9 /// 連続測定を開始 10 /// ch: 0 (1chのみ)、1(2chのみ)、2(1, 2ch同時) 11 /// PreLen: プレトリガ長 12 /// TrigType: トリガ種類、0: 内部トリガ、1: 外部デジタル、2: アナログ立ち上がり、3: アナログ立下り 13 /// TrigCh: アナログトリガのトリガチャネル、0: ch1, 1: ch2 14 pub fn TUSB0216AD_Start(id: i32, ch: &u8, PreLen: i32, TrigType: u8, TrgCh: u8) -> i32; 15 /// 連続取り込み停止 16 pub fn TUSB0216AD_Stop(id: i32) -> i32; 17 /// 取り込み済みデータを取得 18 /// data: 取り込みデータの格納先のポインタ 19 /// datalen: 取り込み要求長。1~262144、戻るときには実際に取得された数が入っている 20 pub fn TUSB0216AD_Ad_Data(id: i32, ch: u8, data: *mut i32, datalen: *mut u32) -> i32; 21 /// クロック時間の設定 22 /// ClkTime: 内部クロック周期設定、500 ~ 2147483647。クロック周期 = ClkTime * 20 ns 23 /// sel: クロックソース、0: 内部クロック、1: 外部クロック 24 pub fn TUSB0216AD_AdClk_Set(id: i32, ClkTime: i32, sel: u8) -> i32; 25 /// ソフトウェアトリガを掛ける 26 pub fn TUSB0216AD_Trigger(id: i32) -> i32; 27} 28 29pub fn continuous_read(id: i32, seconds: u64, flag: Arc<Mutex<i8>>) { 30 println!("Timer start!"); 31 let sleeping_time = time::Duration::from_secs(seconds); 32 33 unsafe { 34 TUSB0216AD_Input_Set(id, 0, 0); 35 TUSB0216AD_Start(id, &(2 as u8), 0, 0, 0); 36 TUSB0216AD_Trigger(id); 37 } 38 39 *flag.lock().unwrap() = 0; // 計測開始のフラグを立てる 40 thread::sleep(sleeping_time); 41 42 unsafe { 43 TUSB0216AD_Stop(id); 44 } 45 46 *flag.lock().unwrap() = 1; // 計測終了のフラグを立てる 47 println!("Timer stopped"); 48} 49 50/// CH1, CH2 からのデータを取得する 51pub fn get_data( 52 id: i32, 53 flag: Arc<Mutex<i8>>, 54 _position: &mut Vec<f32>, 55 _intensity: &mut Vec<f32>, 56) { 57 const MAX_LENGTH: usize = 262142; 58 let mut length = MAX_LENGTH as u32; 59 60 println!("Data acquisition started"); 61 loop { 62 if *flag.lock().unwrap() != -1 { 63 break; 64 } 65 thread::sleep(time::Duration::from_millis(10)); 66 } 67 68 let mut data1 = [0; MAX_LENGTH]; 69 let mut data2 = [0; MAX_LENGTH]; 70 let l_ptr = &mut length as *mut u32; 71 72 loop { 73 if *flag.lock().unwrap() == 1 { 74 break; 75 } 76 77 // 問題の部分!!!!!!!!!!!! 78 unsafe { 79 TUSB0216AD_Ad_Data(id, 1, data1.as_mut_ptr(), l_ptr); 80 TUSB0216AD_Ad_Data(id, 2, data2.as_mut_ptr(), l_ptr); 81 } 82 83 84 } 85 println!("Data acquisition stopped"); 86}

lib.rs中の runを実行すると、

Timer start! Data acquisition started Timer stopped Data acquisition stopped

という文字列が現れることが期待できます。実際、TUSB0216AD_Ad_Data(id, 1, data1.as_mut_ptr(), l_ptr)TUSB0216AD_Ad_Data(id, 2, data2.as_mut_ptr(), l_ptr)の一方のみの時には期待通りの動作をします。しかし上のように2つ並べて書くと、

Timer start!

だけ表示されてプロセスが何のエラーも吐かずに終わってしまいます。

全く自分では原因がわからず困っています。よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

いま手元にWindows環境がないので試してみることができないのですが、怪しいのは以下のところでしょうか。

helper.rs

rust

1 const MAX_LENGTH: usize = 262142; 2 3 let mut data1 = [0; MAX_LENGTH]; 4 let mut data2 = [0; MAX_LENGTH];

Rustの配列はスタック上にアロケートされます。0i32型の値が262142個入る配列が2つですから、4 * 262142 * 2で2097136バイト(約2MiB)のメモリーが必要です。

しかしスタックのサイズは限られています。Windowsでどのくらいの領域が確保されるのか知らないのですが、2MiBではスタック領域を大きくはみ出ている可能性があります。(スタックがオーバーフローしている)

data1data2の2つの配列があったときは以下のようになりそうです。

  • 片方の配列の先頭バイトはスタック領域内にあり、途中からスタック領域外にはみ出している
  • もう一方の配列は先頭から最後までの全体がスタック領域外にある

TUSB0216AD_Ad_Dataは配列の先頭の4バイトにアクセスしていますので、上でいう「もう一方の配列」の先頭の4バイトにアクセスしたときにメモリーアクセス違反のようなエラーが起きて、プログラムが強制終了させられると思います。

rust

1unsafe { 2 TUSB0216AD_Ad_Data(id, 1, data1.as_mut_ptr(), l_ptr); 3 TUSB0216AD_Ad_Data(id, 2, data2.as_mut_ptr(), l_ptr); 4 }

ではなぜTUSB0216AD_Ad_Data片方だけを書くとうまくいくのかというと、多分、質問者さんがreleaseモードでコンパイルしているのではないでしょうか?

たとえば以下のようにdata1だけにアクセスしてdata2にアクセスしない場合、releaseモードでコンパイルすると、最適化によってdata2に関係するコードが全て削除されます。配列が1つだけなら、その先頭アドレスはスタック領域に入っているので動くのだと思います。

rust

1unsafe { 2 TUSB0216AD_Ad_Data(id, 1, data1.as_mut_ptr(), l_ptr); 3 }

試しにMAX_LENGTH100のような小さな数字にして実行してみてください。もしそれで解決したら、スタックオーバーフローが起きているということになります。その場合は配列を使うのをやめて、ヒープにアロケートされるVec<u32>を使ってください。

訂正

data1data2の2つの配列があったときは以下のようになりそうです。

  • 片方の配列の先頭バイトはスタック領域内にあり、途中からスタック領域外にはみ出している
  • もう一方の配列は先頭から最後までの全体がスタック領域外にある

Rustは配列を初期値で埋めますので、上のようになっていると、配列が1つしかないときでもメモリーアクセス違反のようなエラーが起きてしまいます。ということは、上は間違いで、下のようになっているのかもしれません。

  • 片方の配列は先頭から最後までの全体がスタック領域内にある
  • もう一方の配列の先頭バイトはスタック領域内にあり、途中からスタック領域外にはみ出している

そして、配列が2つあったときは、TUSB0216AD_Ad_Dataを呼ぶところではなく、配列を初期化するところでメモリーアクセス違反のようなエラーが起きているのかもしれません。

投稿2021/08/07 12:56

編集2021/08/07 13:03
tatsuya6502

総合スコア2035

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Yhaya

2021/08/07 13:12 編集

ご回答ありがとうございます。試してみましたところ、data1, 2のサイズを小さくしてみるとエラーが出なくなりました。262142はだめで260000までは正常に動きましたのでどうやらスタック領域は2MiBなようです。ありがとうございました。
tatsuya6502

2021/08/07 13:18

なるほど、少しだけはみ出していたのですね。解決してよかったです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問