-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathffi-chap.xml
More file actions
424 lines (311 loc) · 35.3 KB
/
ffi-chap.xml
File metadata and controls
424 lines (311 loc) · 35.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
<?xml version="1.0" encoding="UTF-8"?>
<!-- FFI docs as a chapter -->
<chapter id="ffi">
<title>外部関数インタフェース(FFI)</title>
<para>GHCは、Haskellの外部関数インタフェースに(大部分)準拠している。これの定義は<ulink url="http://www.haskell.org/"><literal>http://www.haskell.org/</literal></ulink>にあるHaskellレポートの一部である。</para>
<para>FFI対応はデフォルトで有効であるが、<option>-XForeignFunctionInterface</option><indexterm><primary><option>-XForeignFunctionInterface</option></primary> </indexterm>で明示的に有効にしたり無効にしたりできる。</para>
<para>GHCには、FFI追補に対する固有の拡張がいくつか実装されている。これらの拡張は<xref linkend="ffi-ghcexts"/>に記述されている。これらの拡張を使ったプログラムは可搬でないということにどうか気を付けてほしい。従って、これらの機能は可能な限り避けるべきである。</para>
<para>FFIライブラリの説明は一緒に配布されているライブラリ説明書にある。例えば<ulink url="&libraryBaseLocation;/Control-Concurrent.html"><literal>Foreign</literal></ulink>モジュールを見よ。</para>
<sect1 id="ffi-ghcexts">
<title>FFI追補に対するGHCの拡張</title>
<para>この節で説明するFFIの機能はGHC特有のものである。これらを使った場合、他のコンパイラに対する可搬性がなくなる。</para>
<sect2>
<title>非ボックス化型</title>
<para>基本外部型(FFI追補の3.2節を見よ)として、以下の型を使える。<literal>Int#</literal>、<literal>Word#</literal>、<literal>Char#</literal>、<literal>Float#</literal>、<literal>Double#</literal>、<literal>Addr#</literal>、<literal>StablePtr# a</literal>、<literal>MutableByteArray#</literal>、<literal>ForeignObj#</literal>、<literal>ByteArray#</literal>。</para>
</sect2>
<sect2 id="ffi-newtype-io">
<title>IOモナドを包むnewtype</title>
<para>FFIの仕様は、種々の箇所にIOモナドが現れることを要求するが、次のようにIOモナドを<literal>newtype</literal>で包むのが便利なことがある
<programlisting>
newtype MyIO a = MIO (IO a)
</programlisting>
(このようなことをする理由としては、例えば、プログラムのある場所において、任意のIO手続きを呼ばれることを防ぎたい、というのが考えられる)
</para>
<para>Haskell FFIは既に、外部にインポート・エクスポートされるものの引数や結果がnewtypeだった場合、それらは自動的に外される、としている(FFI追補の3.2節)。GHCはこれを拡張して、IOモナド自体を包むnewtypeも自動的に外す。より正確にいうと、FFIの仕様がIO型を要求しているところならどこでも、IO型をnewtypeで包んだ物も認める。例えば、以下の宣言は問題ない。
<programlisting>
foreign import foo :: Int -> MyIO Int
foreign import "dynamic" baz :: (Int -> MyIO Int) -> CInt -> MyIO Int
</programlisting>
</para>
</sect2>
<sect2 id="ffi-prim">
<title>プリミティブのインポート</title>
<para>GHCはFFIを拡張して、<literal>prim</literal>という呼び出し規約を追加している。例をあげる。
<programlisting>
foreign import prim "foo" foo :: ByteArray# -> (# Int#, Int# #)
</programlisting>
これは、GHCの内部的な呼び出し規約に従う、Cmmコードで書かれた関数群をインポートするのに使う。この機能は、専らGHC付属の中核ライブラリでのみ使うことを意図している。さらなる詳細は<ulink linkend="http://ghc.haskell.org/trac/ghc/wiki/Commentary/PrimOps">GHC開発者wiki</ulink>を見よ。</para>
</sect2>
<sect2 id="ffi-interruptible">
<title>割り込み可能な外部呼び出し</title>
<para>これは、外部呼び出しと<literal>Control.Concurrent.throwTo</literal>の相互作用についてである。通常、<literal>throwTo</literal>の対象が外部呼び出しの実行中であった場合、その呼び出しが完了するまで例外が発生せず、その間呼び出し元はブロックされる。これによってプログラムが無反応になることがあり、特にユーザによる割り込み(Control-Cなど)の場合に問題になる。Control-Cシグナルを受け取ったとき(Unixなら<literal>SIGINT</literal>)のデフォルトの振る舞いは、主スレッドに<literal>UserInterrupt</literal>例外を発生させることである。その時点で主スレッドが外部関数呼び出しでブロックしていた場合、プログラムはユーザによる割り込みに反応しない。</para>
<para>問題は、一般に外部呼び出しに安全に割り込むことが不可能であることだ。しかしなお、ブロックするシステムコールに割り込む方法をGHCは提供しており、UnixとWindowsの両方で、大部分のシステムコールに対して動作する。<literal>InterruptibleFFI</literal>拡張が有効である場合、外部呼び出しに<literal>safe</literal>や<literal>unsafe</literal>の代わりに<literal>interruptible</literal>という注釈を付けることができる。
<programlisting>
foreign import ccall interruptible
"sleep" sleepBlock :: CUint -> IO CUint
</programlisting>
<literal>interruptible</literal>は<literal>safe</literal>と同様に振る舞うが、違いは、interruptibleな外部呼び出しを実行中のスレッドが<literal>throwTo</literal>の対象になった時、OS依存の機構を使って外部呼び出しが返るように仕向けようとする点である。
<variablelist>
<varlistentry>
<term>Unixシステム</term>
<listitem>
<para>外部呼び出しを行っているスレッドに対して<literal>SIGPIPE</literal>シグナルが送られる。通常、これだけでブロック中のシステムコールが<literal>EINTR</literal>で返ることになる(GHCはデフォルトで空のシグナルハンドラを<literal>SIGPIPE</literal>に設定し、デフォルトの挙動(プロセスの終了)を上書きしている)。</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Windowsシステム</term>
<listitem>
<para>
[Vista以降のみ] RTSが<literal>CancelSynchronousIO</literal>というWin32関数を呼び、これが、ブロックしているI/O操作をエラー<literal>ERROR_OPERATION_ABORTED</literal>で終了させる。</para>
</listitem>
</varlistentry>
</variablelist>
システムコールが成功裏に割り込まれると、それはHaskellに返り、そこで例外が発生する。<literal>interruptible</literal>を使う場合には、この外部関数<!-- 訳注: システムコールの間違いか-->の呼び出し元が、呼び出しが割り込まれた結果を正しく扱えるようになっているかどうか特に注意すること。Unixでは<literal>EINTR</literal>を常に検査するのは良い習慣であるが、Windowsでは通常は<literal>ERROR_OPERATION_ABORTED</literal>に対処することが必要でない。</para>
</sect2>
<sect2 id="ffi-capi">
<title>呼び出し規約CAPI</title>
<para>拡張<literal>CApiFFI</literal>は、foreign宣言中で<literal>capi</literal>という呼び出し規約を使うことを可能にする。例えば、
<programlisting>
foreign import capi "header.h f" f :: CInt -> IO CInt
</programlisting>
プラットフォームのABIに従って<literal>f</literal>への呼び出しを生成する代わりに、ここでは<literal>header.h</literal>で定義されたCのAPIを使って<literal>f</literal>を呼ぶ。これにより、<literal>f</literal>が正規の関数でなくCPPマクロとして定義されていたとしてもこれを呼ぶことができる。
</para>
<para>
<literal>capi</literal>を使うとき、(関数でなく)値をインポートすることができる。例えば、
<programlisting>
foreign import capi "pi.h value pi" c_pi :: CDouble
</programlisting>
は、<literal>pi</literal>が
<programlisting>
const double pi = 3.14;
</programlisting>
と定義されていても、
<programlisting>
#define pi 3.14
</programlisting>
と定義されていても動作する。
</para>
<para>Haskellの型に対応するCの型をGHCに教えるために、型定義に<literal>CTYPE</literal>プラグマを付けることができる。その型が定義されているヘッダを指定することもできる。構文は次のようになる。
<programlisting>
data {-# CTYPE "unistd.h" "useconds_t" #-} T = ...
newtype {-# CTYPE "useconds_t" #-} T = ...
</programlisting>
</para>
</sect2>
<sect2>
<title><literal>hs_thread_done()</literal></title>
<programlisting>
void hs_thread_done(void);
</programlisting>
<para>スレッドが<literal>foreign export</literal>を介してHaskellの関数を呼ぶ際、GHCは少量のスレッドローカル・メモリを確保する。通常このメモリは<literal>hs_exit()</literal>まで解放されない。以降のHaskellへの呼び出しを高速で行なうために、このメモリはキャッシュされるのである。しかし、アプリケーションが長時間走り続けるものであり、Haskellを呼ぶスレッドを繰り返し作るのなら、おそらく、Haskell関数を呼び終わったスレッドにはこのメモリを解放させる方が良いだろう。そのためには、メモリを解放したいスレッドから<literal>hs_thread_done()</literal>を呼べば良い。</para>
<para><literal>hs_thread_done()</literal>を呼ぶか呼ばないかは完全に自由である。好きなだけの回数呼んで良い。Haskellの関数を呼んだことがない、あるいはこれから呼ぶ予定がないスレッドからこれを呼んでも安全である。これを呼ぶのを忘れた場合、起こり得る最悪の事態は、多少のメモリが<literal>hs_exit()</literal>が呼ばれるまで確保されたままになることである。これを必要以上に多く呼んだ場合、起こり得る最悪の事態は、Haskell関数を次に呼んだ時に多少のオーバーヘッドが発生することである。</para>
</sect2>
</sect1>
<sect1 id="ffi-ghc">
<title>GHCでFFIを使う</title>
<para>以下の各節では、外部関数インタフェースをGHCで使うに当たってのヒントを与える。</para>
<sect2 id="foreign-export-ghc">
<title>GHCで<literal>foreign export</literal>と<literal>foreign import ccall "wrapper"</literal>を使う</title>
<indexterm><primary><literal>foreign export
</literal></primary><secondary>with GHC</secondary>
</indexterm>
<para><literal>foreign export</literal>または<literal>foreign import "wrapper"</literal>を使ったモジュール(<filename>M.hs</filename>としよう)をコンパイルするとき、GHCは<filename>M_stub.c</filename>と<filename>M_stub.h</filename>の二つのファイルを生成する。同時に、GHCは、<filename>M_stub.c</filename>を自動的にコンパイルして、<filename>M_stub.o</filename>を生成する。</para>
<para>単純な<literal>foreign export</literal>の場合、<filename>M_stub.h</filename>にはforiegn exportされた関数のプロトタイプが入り、<filename>M_stub.c</filename>には定義が入る。例えば、以下のモジュールをコンパイルしたとする。</para>
<programlisting>
module Foo where
foreign export ccall foo :: Int -> IO Int
foo :: Int -> IO Int
foo n = return (length (f n))
f :: Int -> [Int]
f 0 = []
f n = n:(f (n-1))</programlisting>
<para>すると、<filename>Foo_stub.h</filename>の中身は大体次のようなものになる。</para>
<programlisting>
#include "HsFFI.h"
extern HsInt foo(HsInt a0);</programlisting>
<para>そして、<filename>Foo_stub.c</filename>には、コンパイラによって生成された<literal>foo()</literal>の定義が置かれる。Cから<literal>foo()</literal>を呼ぶには、<literal>#include "Foo_stub.h"</literal>として<literal>foo()</literal>を呼ぶだけで良い。</para>
<para><filename>foo_stub.c</filename>と<filename>foo_stub.h</filename>は<option>-stubdir</option>を使って別の場所に生成するようにできる。<xref linkend="options-output"/>を見よ。</para>
<para>プログラムをリンクするとき、最終的なリンクのコマンド行に<filename>M_stub.o</filename>を含めるのを忘れないように。これをしないと、関数が足りない旨のエラーが発生する。(これは、<literal>ghc --make</literal>でプログラムをビルドする時には不要である。GHCが自動的に必要なものをリンクするので)</para>
<sect3 id="using-own-main">
<title>自分で用意した<literal>main()</literal>を使う</title>
<para>通常、GHCのランタイムシステムが<literal>main()</literal>を提供する。この<literal>main()</literal>はHaskellプログラムの<literal>Main.main</literal>を起動するように手配する。しかし、main関数を別の言語(例えばC)で書いて、そこにHaskellコードをリンクしなければならないこともあるだろう。これをするためには、明示的にHaskellランタイムシステムを初期化しなければならない。</para>
<para>上の例を使って、これをスタンドアローンのCプログラムから呼び出してみよう。以下がCコードである。</para>
<programlisting>
#include <stdio.h>
#include "HsFFI.h"
#ifdef __GLASGOW_HASKELL__
#include "foo_stub.h"
#endif
int main(int argc, char *argv[])
{
int i;
hs_init(&argc, &argv);
for (i = 0; i < 5; i++) {
printf("%d\n", foo(2500));
}
hs_exit();
return 0;
}</programlisting>
<para>GHC特有の部分は<literal>#ifdef __GLASGOW_HASKELL__</literal>で囲った。それ以外のコードは、FFI標準に対応したHaskell実装の間で可搬であるはずだ。</para>
<para><literal>hs_init()</literal>の呼び出しでGHCのランタイムシステムが初期化される。<literal>hs_init()</literal>を呼ぶ前にはいかなるHaskell関数も起動しようと*しない*ように。疑いの余地なく、悪いことが起こるだろう。</para>
<para><literal>hs_init()</literal>に<literal>argc</literal>と<literal>argv</literal>への参照を渡している。これは、RTSへの引数(つまり、<literal>+RTS...-RTS</literal>の間の引数)を抽出できるようにするためである。</para>
<para>Haskellの関数を呼び出し終えた後、<literal>hs_exit()</literal>を呼んでRTSを終了させることができる。</para>
<para><literal>hs_init()</literal>を複数回呼んでも良いが、毎回それに対応して一回(だけ)<literal>hs_exit()</literal><footnote><para>実際にシステムを脱初期化するのは最も外側の<literal>hs_exit()</literal>である。これが起きた後、GHCのランタイムシステムを信頼性のある方法で再初期化することは今のところできないことに注意。<xref linkend="infelicities-ffi" />を見よ。</para></footnote>を呼ぶ必要がある。</para>
<para>参考: 最終的なプログラムをリンクする際、通常最も簡単なのは、GHCを使ってリンクすることである。(絶対に必要な訳ではないが)。GHCを使うなら、フラグ<option>-no-hs-main</option><indexterm><primary><option>-no-hs-main</option></primary></indexterm>を使うことを忘れないように。さもないと、GHCは<literal>Main</literal>というHaskellモジュールをリンクしようとする。</para>
<para><literal>hs_init()</literal>とともに<literal>+RTS</literal>フラグを使うには、例に多少の変更を加えなければならない。デフォルトでは、GHCのRTSは「安全な」<literal>+RTS</literal>フラグ(<xref linkend="options-linker" />を見よ)のみを受け付け、これを変えるにはリンク時フラグ<option>-rtsopts</option><indexterm><primary><option>-rtsopts</option></primary></indexterm>を使う。しかし<option>-rtsopts</option>は<option>-no-hs-main</option>が使われているときには効果を持たない(<option>-with-rtsopts</option>も同様)。これらのオプションを設定するには、<literal>hs_init()</literal>の代わりにGHC固有のAPIを呼ぶ必要がある。</para>
<programlisting>
#include <stdio.h>
#include "HsFFI.h"
#ifdef __GLASGOW_HASKELL__
#include "foo_stub.h"
#include "Rts.h"
#endif
int main(int argc, char *argv[])
{
int i;
#if __GLASGOW_HASKELL__ >= 703
{
RtsConfig conf = defaultRtsConfig;
conf.rts_opts_enabled = RtsOptsAll;
hs_init_ghc(&argc, &argv, conf);
}
#else
hs_init(&argc, &argv);
#endif
for (i = 0; i < 5; i++) {
printf("%d\n", foo(2500));
}
hs_exit();
return 0;
}</programlisting>
<para>変更点が二つある。まず<literal>Rts.h</literal>をインクルードした。これはGHC固有の外部RTSインタフェースを定義する。そして<literal>hs_init_ghc()</literal>を呼ぶ代わりに、 <literal>RtsConfig</literal>型の引数を渡して<literal>hs_init_ghc()</literal>を呼んだ。<literal>RtsConfig</literal>は、ランタイムシステムの振る舞いに影響を与えるいろいろなフィールドを持つ構造体である。これの定義は以下。</para>
<programlisting>
typedef struct {
RtsOptsEnabledEnum rts_opts_enabled;
const char *rts_opts;
} RtsConfig;
extern const RtsConfig defaultRtsConfig;
typedef enum {
RtsOptsNone, // +RTSはエラーになる
RtsOptsSafeOnly, // 安全なRTSオプションが許される。それ以外はエラー
RtsOptsAll // 全てのRTSオプションが許される
} RtsOptsEnabledEnum;
</programlisting>
<para>デフォルト値として<literal>defaultRtsConfig</literal>があり、<literal>RtsConfig</literal>型の変数の初期化に用いられるべきである。将来、<literal>RtsConfig</literal>にもっと多くのフィールドが加えられることは間違いないので、コードを前方互換に保つためには、上のコード例のように<literal>defaultRtsConfig</literal>で初期化してから必要なフィールドを変更するのが良い。</para>
</sect3>
<sect3 id="ffi-library">
<title>外部のコードから呼べるようなHaskellライブラリを作る</title>
<para><xref linkend="using-own-main"/>とよく似た状況だが、完全なプログラムをリンクするのが目的ではなく、Haskellコードからライブラリを作って、Cのコードでできたライブラリと同じように使えるようにしたいという場合である。</para>
<para>この場合、まず、Haskellコードが最初に呼ばれる前にランタイムが初期化される必要がある。このため、初期化と脱初期化のためのエントリポイントをCかC++で実装し、ライブラリ内に用意するべきである。例えば以下のように。</para>
<programlisting>
#include <stdlib.h>
#include "HsFFI.h"
HsBool mylib_init(void){
int argc = 2;
char *argv[] = { "+RTS", "-A32m", NULL };
char **pargv = argv;
// Haskellのラインタイムを初期化する
hs_init(&argc, &pargv);
// その他の初期化があればここで行ない、
// 問題があればfalseを返す
return HS_BOOL_TRUE;
}
void mylib_end(void){
hs_exit();
}
</programlisting>
<para>この初期化ルーチン<literal>mylib_init</literal>は、通常どおり<literal>hs_init()</literal>を呼んでHaskellランタイムを初期化する。これに対応する脱初期化関数<literal>mylib_end()</literal>は<literal>hs_exit()</literal>を呼んでランタイムを終了させる。</para>
</sect3>
</sect2>
<sect2 id="glasgow-foreign-headers">
<title>ヘッダファイルを使う</title>
<indexterm><primary>C calls, function headers</primary></indexterm>
<para>Cの関数は、通常、Cのヘッダファイルでプロトタイプを使って宣言される。前の版のGHC(6.8.3以前)は、Haskellコードから生成されるCソースファイルにヘッダを<literal>#include</literal>していたので、FFIを介して呼ばれるCの関数が正しい型で呼ばれているかどうかを、Cコンパイラが検査することができた。</para>
<para>GHCはもはや、Cを介してコンパイルするときに外部のヘッダファイルをインクルードしないので、この検査は行われない。この変更は、<ulink linkend="native-code-gen">ネイティブコードのバックエンド</ulink>(<literal>-fasm</literal>)との互換性、およびFFI仕様への準拠のために為された。(FFI仕様によると、FFI呼び出しは、Cのヘッダファイルを使ったときに起き得る、マクロ展開その他のCPPによる変換に影響されないことが要求される)。また、この方針によって、外部呼び出しをモジュール境界やパッケージ境界を越えてインライン化する作業が単純になる。インライン展開された外部呼び出しをコンパイルするときにそのヘッダファイルが存在する必要がないため、どんな文脈においてもコンパイラが自由に外部呼び出しをインライン化できるようになるからである。</para>
<para><literal>-#include</literal>オプションはもはや非推奨であり、Cabalのパッケージ仕様の中の<literal>include-files</literal>フィールドは無視される。</para>
</sect2>
<sect2>
<title>メモリ確保</title>
<para>FFIで使うためのメモリ確保については、複数の方法がFFIライブラリで提供されていて、どれを使うのがいいかはっきりしないことがある。これを決めるには、個々の確保方法が特定のコンパイラ・プラットフォームでどの程度効率的かを勘案しなければならないことがあるので、この節では、GHCでのこれらの確保方法の性能がどれほどかをある程度明らかにすることを目標とする。</para>
<variablelist>
<varlistentry>
<term><literal>alloca</literal>とその仲間</term>
<listitem>
<para>特定の<literal>IO</literal>計算でのみ利用可能なものとして、短期的な確保をするときに便利である。この種の確保はFFI関数との間でデータをmarshalするときに良く使われる。</para>
<para>GHCでは、<literal>alloca</literal>は<literal>MutableByteArray#</literal>で実装されているので、確保・開放は速いはずである。Cの<literal>malloc/free</literal>よりはずっと速いが、Cのスタック確保ほどには速くない。可能な限り<literal>alloca</literal>を使うと良い。</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>mallocForeignPtr</literal></term>
<listitem>
<para>GCを必要とする、中長期にわたる確保に便利である。ただし、外部のデータ構造の中にメモリへのポインタを保持したいなら、<literal>mallocForeignPtr</literal>は良い選択では<emphasis>ない</emphasis>。</para>
<para>GHCでは、<literal>mallocForeignPtr</literal>もまた<literal>MutableByteArray#</literal>で実装されている。メモリを指すのは<literal>ForeignPtr</literal>だが、実際には終了子が関係せず(<literal>addForeignPtrFinalizer</literal>で追加しない限り)、開放はGCが行うので、<literal>mallocForeignPtr</literal>は通常とても安価である。</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>malloc/free</literal></term>
<listitem>
<para>他が全て失敗したなら、<literal>Foreign.malloc</literal>と<literal>Foreign.free</literal>に頼る必要がある。これらは同名のC関数のラッパに過ぎない。よって、効率性は結局プラットフォームのCライブラリにあるこれらの関数の実装に依存する。我々の経験では通常、<literal>malloc</literal>と<literal>free</literal>は上に挙げた確保方法に比べてずっと遅い。</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>Foreign.Marshal.Pool</literal></term>
<listitem>
<para>プールは今のところ<literal>malloc/free</literal>を使って実装されているので、メモリを構造化するという点では他の方法より便利かもしれないが、効率的だということはない。ただし、より高性能のプールの実装を計画はしている。</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
<sect2 id="ffi-threads">
<title>マルチスレッドとFFI</title>
<para>マルチスレッド環境でFFIを使うには、<option>-threaded</option>オプションを使う必要がある。(<xref linkend="options-linker" />を見よ)</para>
<sect3>
<title>foreign importとマルチスレッド</title>
<para><literal>safe</literal>注釈(デフォルトである)付きで<literal>foreign import</literal>された関数を呼ぶ場合、プログラムが<option>-threaded</option>を使ってリンクされているなら、その呼び出しは他のHaskellスレッドと並行に走る。プログラムが<option>-threaded</option>なしでリンクされている時は、その呼び出しが返るまで他のHaskellスレッドはブロックされる。</para>
<para>つまり、長い時間が掛かったり、いつまでブロックするか分からない関数を外部呼び出しする必要があるなら、それを<literal>safe</literal>にして、<option>-threaded</option>を使うべきである。ライブラリ関数の中には、内部的にこの種の呼び出しを行うものもある。その場合は説明文書にその旨記載があるべきである。</para>
<para>複数のHaskellスレッドから外部呼び出しを行い、しかも<option>-threaded</option>を使っている場合、呼んでいる外部コードがスレッドセーフでなければならないことに注意すること。特に、ある種のGUIライブラリはスレッドセーフでなく、GUIメソッドを一つのスレッドからのみ呼ぶことを要求する。この場合、GUI操作は一つのHaskellスレッドからのみ行い、さらに結合スレッド(<xref linkend="haskell-threads-and-os-threads"/>を見よ)を使う必要があるかもしれない。</para>
<para>複数のHaskellスレッドでなされた外部呼び出しは<emphasis>並列</emphasis>に実行され得るのに注意。これは<literal>+RTS -N</literal>フラグ(<xref linkend="parallel-options"/>)を使っていない場合でもそうである。<literal>+RTS -N</literal>フラグはHaskellスレッドの並列実行を制御するが、ある時点での外部の呼び出しは、<literal>+RTS -N</literal>の値に関係なく、いくらでも多くなり得る。</para>
<para>呼び出しが<literal>interruptible</literal>とされており、プログラムがマルチスレッドの場合、そのHaskellスレッドが例外を受けた場合に呼び出しが中断され得る。この中断が発生する機構はプラットフォームによって異なるが、ブロックしているシステムコールが「割り込み発生」のエラーコードで即座に返ることが意図されている。使われているOSスレッドを破壊するものではない。さらなる詳細は<xref linkend="ffi-interruptible"/>を見よ。</para>
</sect3>
<sect3 id="haskell-threads-and-os-threads">
<title>HaskellスレッドとOSスレッドの関係</title>
<para>通常、HaskellスレッドとOSスレッドの間に固定された関係はない。つまり、外部呼び出しを行った時、それがどのOSスレッドで起こるのかは規定されない。さらに、一つのHaskellスレッドが複数回の呼び出しを行ったとしても、それが同じOSスレッド上で実行される保証はない。</para>
<para>このことは普通問題にならないし、このおかげでGHCのランタイムシステムがOSのスレッド資源を効率的に利用することが可能になっている。しかし、例えばスレッドローカルな状態を使う外部コードを呼ぶ場合など、どのOSスレッドを使うかをもっと制御できた方が便利なことがある。このような場合のために、<emphasis>結合スレッド</emphasis>(bound thread)を用意している。これは、特定のOSスレッドに結び付けられたHaskellスレッドである。結合スレッドについての情報は、<ulink url="&libraryBaseLocation;/Control-Concurrent.html"><literal>Control.Concurrent</literal></ulink>モジュールの説明を見よ。</para>
</sect3>
<sect3>
<title>foreign exportとマルチスレッド</title>
<para>プログラムが<option>-threaded</option>付きでリンクされているなら、<literal>foreign export</literal>された関数を複数のOSスレッドから並行に呼び出してよい。通常通り、ランタイムシステムは<literal>hs_init()</literal>を呼んで初期化せねばならない。また、これらの呼び出しは<literal>foreign export</literal>されたいかなる関数をも呼び出す前に完了していなければならない。</para>
</sect3>
<sect3 id="hs-exit">
<title><literal>hs_exit()</literal>の使用について</title>
<para>通常、<literal>hs_exit()</literal>によって、そのシステムで実行中のすべてのHaskellスレッドは終了し、<literal>hs_exit()</literal>が返る時点では、もはやHaskellスレッドは一つも走っていない。この後、ランタイムは、必要ならプロファイル出力や統計情報を生成したり、持っているメモリをすべて開放したりして、システムを規則正しく終了させる。</para>
<para>Haskellスレッドを強制的に終了させることができない場合もある。例えば、スレッドが外部呼び出しを実行中かもしれないが、外部の呼び出しを終了させる方法はない。さらに、ランタイムは、Haskellコードとランタイムがメモリから削除予定であるという最悪の場合を想定しなければならない。(例えば、<link linkend="win32-dlls">WindowsのDLL</link>では、<literal>hs_exit()</literal>が呼ばれるのは、通常そのDLLをアンロードする前である)。そのため、<literal>hs_exit()</literal>は、実行中のすべての外部呼び出しが返るまで待ってからでないと、自分自身返ることができない。</para>
<para>要するに、外部呼び出しでブロックしているHaskellスレッドがあるなら、その呼び出しが返るまで<literal>hs_exit()</literal>はハングする(場合によってはビジーウェイト)ということだ。よって、<literal>hs_exit()</literal>を呼び出すときにそのようなスレッドがシステム中にないようにするのが良い。これには、I/Oをしているあらゆるスレッドも含まれる。I/Oはブロックする外部呼び出しを使って実装されているかもしれない(プラットフォームとI/Oの種類によってはそうでないかもしれない)からである。</para>
<para>GHCのランタイムは、プログラムが終了する場合を特別扱いして、スタンドアローンの実行ファイルが終了するときにブロックしているスレッドを待つ必要がないようにしている。コードがメモリから取り除かれるのと同時にプログラムとそのすべてのスレッドが終了するのだから、スレッドが先に終了するように保証する必要がない。(非公式だが、この、早くていい加減な<literal>hs_exit()</literal>が欲しいなら、代わりに<literal>shutdownHaskellAndExit()</literal>を呼ぶと良い)</para>
</sect3>
</sect2>
<sect2 id="ffi-floating-point">
<title>浮動小数点とFFI</title>
<para>C99標準の<literal>fenv.h</literal>ヘッダは、浮動小数点ユニットの状態を確認したり変更したりするための操作を提供している。特に、浮動小数点演算で使われる丸めモードを変更したり、例外フラグを検査したりすることができる。</para>
<para>Haskellでは浮動小数点演算は純粋な型を持ち、その評価順は規定されない。<literal>fenv.h</literal>の関数を使うと浮動小数点演算の結果を変えたりその作用を観測することができるので、厳密には<literal>fenv.h</literal>の使用はプログラム全体の浮動小数点演算の振る舞いを未定義にする。</para>
<para>とはいえ、GHCが浮動小数点状態に関して何を行なうかを厳密に記述することが我々には<emphasis>できる</emphasis>。これは、どうしても<literal>fenv.h</literal>を使わざるを得ないときに、落とし穴についての完全な知識を持っていられるようにするためである。
<itemizedlist>
<listitem>
<para>GHCは浮動小数点環境を完全に無視する。ランタイムはそれを変更しないし、読みもしない。</para>
</listitem>
<listitem>
<para>浮動小数点環境はスレッドの通常のコンテキストスイッチに際して保存されない。なので、あるスレッドで浮動小数点状態を変更すると、その変更が他のスレッドから見えるかもしれない。さらに、例外状態を検査してもその結果は信頼できない。コンテキストスイッチによって変更を受けるかもしれないからである。浮動小数点状態を変更したり検査したりする必要があり、複数スレッドを使う必要もあるなら、bound thread (<literal>Control.Concurrent.forkOS</literal>)を使わねばならない。bound threadは一つのOSスレッドを占有し、OSスレッドが浮動小数点状態の保存と復帰を行なうからである。</para>
</listitem>
<listitem>
<para>外部呼び出しは決してGHCにプリエンプトされないので、外部呼び出しの間に一時的に浮動小数点状態を変更することは安全である。</para>
</listitem>
</itemizedlist>
</para>
</sect2>
</sect1>
</chapter>
<!-- Emacs stuff:
;;; Local Variables: ***
;;; sgml-parent-document: ("users_guide.xml" "book" "chapter") ***
;;; End: ***
-->