forked from erlang/otp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
erl_driver.xml
2979 lines (2919 loc) · 135 KB
/
erl_driver.xml
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
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="latin1" ?>
<!DOCTYPE cref SYSTEM "cref.dtd">
<cref>
<header>
<copyright>
<year>2001</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
The contents of this file are subject to the Erlang Public License,
Version 1.1, (the "License"); you may not use this file except in
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
</legalnotice>
<title>erl_driver</title>
<prepared>Jakob Cederlund</prepared>
<responsible>Jakob Cederlund</responsible>
<docno>1</docno>
<approved></approved>
<checked></checked>
<date>2000-11-27</date>
<rev>PA1</rev>
<file>erl_driver.xml</file>
</header>
<lib>erl_driver</lib>
<libsummary>API functions for an Erlang driver</libsummary>
<description>
<p>An Erlang driver is a library containing a set of native driver
callback functions that the Erlang VM calls when certain
events occur. There may be multiple instances of a driver, each
instance is associated with an Erlang port.</p>
<marker id="WARNING"/>
<warning><p><em>Use this functionality with extreme care!</em></p>
<p>A driver callback is executed as a direct extension of the
native code of the VM. Execution is not made in a safe environment.
The VM can <em>not</em> provide the same services as provided when
executing Erlang code, such as preemptive scheduling or memory
protection. If the driver callback function doesn't behave well,
the whole VM will misbehave.</p>
<list>
<item><p>A driver callback that crash will crash the whole VM.</p></item>
<item><p>An erroneously implemented driver callback might cause
a VM internal state inconsistency which may cause a crash of the VM,
or miscellaneous misbehaviors of the VM at any point after the call
to the driver callback.</p></item>
<item><p>A driver callback that do <seealso marker="#lengthy_work">lengthy
work</seealso> before returning will degrade responsiveness of the VM,
and may cause miscellaneous strange behaviors. Such strange behaviors
include, but are not limited to, extreme memory usage, and bad load
balancing between schedulers. Strange behaviors that might occur due
to lengthy work may also vary between OTP releases.</p></item>
</list>
</warning>
<p>As of erts version 5.5.3 the driver interface has been extended
(see <seealso marker="driver_entry#extended_marker">extended marker</seealso>).
The extended interface introduce
<seealso marker="#version_management">version management</seealso>,
the possibility to pass capability flags
(see <seealso marker="driver_entry#driver_flags">driver flags</seealso>)
to the runtime system at driver initialization, and some new
driver API functions. </p>
<note>
<p>As of erts version 5.9 old drivers have to be recompiled
and have to use the extended interface. They also have to be
adjusted to the
<seealso marker="#rewrites_for_64_bits">64-bit capable driver interface.
</seealso>
</p>
</note>
<p>The driver calls back to the emulator, using the API
functions declared in <c>erl_driver.h</c>. They are used for
outputting data from the driver, using timers, etc.</p>
<p>Each driver instance is associated with a port. Every port
has a port owner process. Communication with the port is normally
done through the port owner process. Most of the functions take
the <c>port</c> handle as an argument. This identifies the driver
instance. Note that this port handle must be stored by the driver,
it is not given when the driver is called from the emulator (see
<seealso marker="driver_entry#emulator">driver_entry</seealso>).</p>
<p>Some of the functions take a parameter of type
<c>ErlDrvBinary</c>, a driver binary. It should be both
allocated and freed by the caller. Using a binary directly avoids
one extra copying of data.</p>
<p>Many of the output functions have a "header buffer", with
<c>hbuf</c> and <c>hlen</c> parameters. This buffer is sent as a
list before the binary (or list, depending on port mode) that is
sent. This is convenient when matching on messages received from
the port. (Although in the latest versions of Erlang, there is
the binary syntax, that enables you to match on the beginning of
a binary.)
<marker id="smp_support"></marker>
</p>
<p>In the runtime system with SMP support, drivers are locked either
on driver level or port level (driver instance level). By default
driver level locking will be used, i.e., only one emulator thread
will execute code in the driver at a time. If port level locking
is used, multiple emulator threads may execute code in the driver
at the same time. There will only be one thread at a time calling
driver call-backs corresponding to the same port, though. In order
to enable port level locking set the <c>ERL_DRV_FLAG_USE_PORT_LOCKING</c>
<seealso marker="driver_entry#driver_flags">driver flag</seealso> in
the <seealso marker="driver_entry">driver_entry</seealso>
used by the driver. When port level locking is used it is the
responsibility of the driver writer to synchronize all accesses
to data shared by the ports (driver instances).</p>
<p>Most drivers written before the runtime system with SMP
support existed will be able to run in the runtime system
with SMP support without being rewritten if driver
level locking is used.</p>
<note>
<p>It is assumed that drivers do not access other drivers. If
drivers should access each other they have to provide their own
mechanism for thread safe synchronization. Such "inter driver
communication" is strongly discouraged.</p>
</note>
<p>Previously, in the runtime system without SMP support,
specific driver call-backs were always called from the same
thread. This is <em>not</em> the case in the runtime system
with SMP support. Regardless of locking scheme used, calls
to driver call-backs may be made from different threads, e.g.,
two consecutive calls to exactly the same call-back for exactly
the same port may be made from two different threads. This
will for <em>most</em> drivers not be a problem, but it might.
Drivers that depend on all call-backs being called in the
same thread, <em>have</em> to be rewritten before being used
in the runtime system with SMP support.</p>
<note>
<p>Regardless of locking scheme used, calls to driver
call-backs may be made from different threads.</p>
</note>
<p>Most functions in this API are <em>not</em> thread-safe, i.e.,
they may <em>not</em> be called from an arbitrary thread. Functions
that are not documented as thread-safe may only be called from
driver call-backs or function calls descending from a driver
call-back call. Note that driver call-backs may be called from
different threads. This, however, is not a problem for any
function in this API, since the emulator has control over
these threads.</p>
<warning>
<p>Functions not explicitly documented as thread safe are
<em>not</em> thread safe. Also note that some functions
are <em>only</em> thread safe when used in a runtime
system with SMP support.</p>
<p>A function not explicitly documented as thread safe may at
some point in time have a thread safe implementation in the
runtime system. Such an implementation may however change to
a thread <em>unsafe</em> implementation at any time <em>without
any notice</em> at all.
</p>
<p><em>Only use functions explicitly documented as thread safe
from arbitrary threads.</em></p>
</warning>
<p><marker id="lengthy_work"/>
As mentioned in the <seealso marker="#WARNING">warning</seealso> text at
the beginning of this document it is of vital importance that a driver callback
does return relatively fast. It is hard to give an exact maximum amount
of time that a driver callback is allowed to work, but as a rule of thumb
a well behaving driver callback should return before a millisecond has
passed. This can be achieved using different approaches.
If you have full control over the code that are to execute in the driver
callback, the best approach is to divide the work into multiple chunks of
work and trigger multiple calls to the
<seealso marker="driver_entry#timeout">timeout callback</seealso> using
zero timeouts. The
<seealso marker="#erl_drv_consume_timeslice"><c>erl_drv_consume_timeslice()</c></seealso>
function can be useful in order to determine when to trigger such
timeout callback calls. It might, however, not always be possible to
implement it this way, e.g. when calling third party libraries. In this
case you typically want to dispatch the work to another thread.
Information about thread primitives can be found below.</p>
</description>
<section>
<title>FUNCTIONALITY</title>
<p>All functions that a driver needs to do with Erlang are
performed through driver API functions. There are functions
for the following functionality:</p>
<taglist>
<tag>Timer functions</tag>
<item>Timer functions are used to control the timer that a driver
may use. The timer will have the emulator call the
<seealso marker="driver_entry#timeout">timeout</seealso> entry
function after a specified time. Only one timer is available
for each driver instance.</item>
<tag>Queue handling</tag>
<item>
<p>Every driver instance has an associated queue. This queue is a
<c>SysIOVec</c> that works as a buffer. It's mostly used for
the driver to buffer data that should be written to a device,
it is a byte stream. If the port owner process closes the
driver, and the queue is not empty, the driver will not be
closed. This enables the driver to flush its buffers before
closing.</p>
<p>The queue can be manipulated from arbitrary threads if
a port data lock is used. See documentation of the
<seealso marker="#ErlDrvPDL">ErlDrvPDL</seealso> type for
more information.</p>
</item>
<tag>Output functions</tag>
<item>With the output functions, the driver sends data back to
the emulator. They will be received as messages by the port owner
process, see <c>open_port/2</c>. The vector function and the
function taking a driver binary are faster, because they avoid
copying the data buffer. There is also a fast way of sending
terms from the driver, without going through the binary term
format.</item>
<tag>Failure</tag>
<item>The driver can exit and signal errors up to Erlang. This is
only for severe errors, when the driver can't possibly keep
open.</item>
<tag>Asynchronous calls</tag>
<item>The latest Erlang versions (R7B and later) has provision for
asynchronous function calls, using a thread pool provided by
Erlang. There is also a select call, that can be used for
asynchronous drivers.</item>
<tag><marker id="multi_threading">Multi-threading</marker></tag>
<item>
<p>A POSIX thread like API for multi-threading is provided. The
Erlang driver thread API only provide a subset of the functionality
provided by the POSIX thread API. The subset provided is
more or less the basic functionality needed for multi-threaded
programming:
</p>
<list>
<item><seealso marker="#ErlDrvTid">Threads</seealso></item>
<item><seealso marker="#ErlDrvMutex">Mutexes</seealso></item>
<item><seealso marker="#ErlDrvCond">Condition variables</seealso></item>
<item><seealso marker="#ErlDrvRWLock">Read/Write locks</seealso></item>
<item><seealso marker="#ErlDrvTSDKey">Thread specific data</seealso></item>
</list>
<p>The Erlang driver thread API can be used in conjunction with
the POSIX thread API on UN-ices and with the Windows native thread
API on Windows. The Erlang driver thread API has the advantage of
being portable, but there might exist situations where you want to
use functionality from the POSIX thread API or the Windows
native thread API.
</p>
<p>The Erlang driver thread API only returns error codes when it is
reasonable to recover from an error condition. If it isn't reasonable
to recover from an error condition, the whole runtime system is
terminated. For example, if a create mutex operation fails, an error
code is returned, but if a lock operation on a mutex fails, the
whole runtime system is terminated.
</p>
<p>Note that there exists no "condition variable wait with timeout" in
the Erlang driver thread API. This is due to issues with
<c>pthread_cond_timedwait()</c>. When the system clock suddenly
is changed, it isn't always guaranteed that you will wake up from
the call as expected. An Erlang runtime system has to be able to
cope with sudden changes of the system clock. Therefore, we have
omitted it from the Erlang driver thread API. In the Erlang driver
case, timeouts can and should be handled with the timer functionality
of the Erlang driver API.
</p>
<p>In order for the Erlang driver thread API to function, thread
support has to be enabled in the runtime system. An Erlang driver
can check if thread support is enabled by use of
<seealso marker="#driver_system_info">driver_system_info()</seealso>.
Note that some functions in the Erlang driver API are thread-safe
only when the runtime system has SMP support, also this
information can be retrieved via
<seealso marker="#driver_system_info">driver_system_info()</seealso>.
Also note that a lot of functions in the Erlang driver API are
<em>not</em> thread-safe regardless of whether SMP support is
enabled or not. If a function isn't documented as thread-safe it
is <em>not</em> thread-safe.
</p>
<p><em>NOTE</em>: When executing in an emulator thread, it is
<em>very important</em> that you unlock <em>all</em> locks you
have locked before letting the thread out of your control;
otherwise, you are <em>very likely</em> to deadlock the whole
emulator. If you need to use thread specific data in an emulator
thread, only have the thread specific data set while the thread is
under your control, and clear the thread specific data before
you let the thread out of your control.
</p>
<p>In the future there will probably be debug functionality
integrated with the Erlang driver thread API. All functions
that create entities take a <c>name</c> argument. Currently
the <c>name</c> argument is unused, but it will be used when
the debug functionality has been implemented. If you name all
entities created well, the debug functionality will be able
to give you better error reports.
</p>
</item>
<tag>Adding / removing drivers</tag>
<item><p>A driver can add and later remove drivers.</p></item>
<tag>Monitoring processes</tag>
<item><p>A driver can monitor a process that does not own a port.</p></item>
<tag><marker id="version_management">Version management</marker></tag>
<item>
<p>Version management is enabled for drivers that have set the
<seealso marker="driver_entry#extended_marker">extended_marker</seealso>
field of their
<seealso marker="driver_entry">driver_entry</seealso>
to <c>ERL_DRV_EXTENDED_MARKER</c>. <c>erl_driver.h</c> defines
<c>ERL_DRV_EXTENDED_MARKER</c>,
<c>ERL_DRV_EXTENDED_MAJOR_VERSION</c>, and
<c>ERL_DRV_EXTENDED_MINOR_VERSION</c>.
<c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> will be incremented when
driver incompatible changes are made to the Erlang runtime
system. Normally it will suffice to recompile drivers when the
<c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> has changed, but it
could, under rare circumstances, mean that drivers have to
be slightly modified. If so, this will of course be documented.
<c>ERL_DRV_EXTENDED_MINOR_VERSION</c> will be incremented when
new features are added. The runtime system uses the minor version
of the driver to determine what features to use.
The runtime system will refuse to load a driver if the major
versions differ, or if the major versions are equal and the
minor version used by the driver is greater than the one used
by the runtime system.</p>
<p>The emulator will refuse to load a driver that does not use
the extended driver interface,
to allow for 64-bit capable drivers,
since incompatible type changes for the callbacks
<seealso marker="driver_entry#output">output</seealso>,
<seealso marker="driver_entry#control">control</seealso> and
<seealso marker="driver_entry#call">call</seealso>
were introduced in release R15B. A driver written
with the old types would compile with warnings and when
called return garbage sizes to the emulator causing it
to read random memory and create huge incorrect result blobs.</p>
<p>Therefore it is not enough to just recompile drivers written with
version management for pre-R15B types; the types have to be changed
in the driver suggesting other rewrites especially regarding
size variables. Investigate all warnings when recompiling!</p>
<p>Also, the API driver functions <c>driver_output*</c>,
<c>driver_vec_to_buf</c>, <c>driver_alloc/realloc*</c>
and the <c>driver_*</c> queue functions were changed to have
larger length arguments and return values. This is a
lesser problem since code that passes smaller types
will get them auto converted in the calls and as long as
the driver does not handle sizes that overflow an <c>int</c>
all will work as before.</p>
</item>
</taglist>
</section>
<section>
<marker id="rewrites_for_64_bits"/>
<title>
REWRITES FOR 64-BIT DRIVER INTERFACE
</title>
<p>
For erts-5.9 two new integer types
<seealso marker="#ErlDrvSizeT">ErlDrvSizeT</seealso> and
<seealso marker="#ErlDrvSSizeT">ErlDrvSSizeT</seealso>
were introduced that can hold 64-bit sizes if necessary.
</p>
<p>
To not update a driver and just recompile it probably works
when building for a 32-bit machine creating a false sense of security.
Hopefully that will generate many important warnings.
But when recompiling the same driver later on for a 64-bit machine
there <em>will</em> be warnings and almost certainly crashes.
So it is a BAD idea to postpone updating the driver and
not fixing the warnings!
</p>
<p>
When recompiling with <c>gcc</c> use the <c>-Wstrict-prototypes</c>
flag to get better warnings. Try to find a similar flag if you
are using some other compiler.
</p>
<p>
Here follows a checklist for rewriting a pre erts-5.9 driver,
most important first.
</p>
<taglist>
<tag>Return types for driver callbacks</tag>
<item>
<p>
Rewrite driver callback
<c><seealso marker="driver_entry#control">control</seealso></c>
to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>.
</p>
<p>
Rewrite driver callback
<c><seealso marker="driver_entry#call">call</seealso></c>
to use return type <c>ErlDrvSSizeT</c> instead of <c>int</c>.
</p>
<note>
<p>
These changes are essential to not crash the emulator
or worse cause malfunction.
Without them a driver may return garbage in the high 32 bits
to the emulator causing it to build a huge result from random
bytes either crashing on memory allocation or succeeding with
a random result from the driver call.
</p>
</note>
</item>
<tag>Arguments to driver callbacks</tag>
<item>
<p>
Driver callback
<c><seealso marker="driver_entry#output">output</seealso></c>
now gets <c>ErlDrvSizeT</c> as 3rd argument instead
of previously <c>int</c>.
</p>
<p>
Driver callback
<c><seealso marker="driver_entry#control">control</seealso></c>
now gets <c>ErlDrvSizeT</c> as 4th and 6th arguments instead
of previously <c>int</c>.
</p>
<p>
Driver callback
<c><seealso marker="driver_entry#call">call</seealso></c>
now gets <c>ErlDrvSizeT</c> as 4th and 6th arguments instead
of previously <c>int</c>.
</p>
<p>
Sane compiler's calling conventions probably make these changes
necessary only for a driver to handle data chunks that require
64-bit size fields (mostly larger than 2 GB since that is what
an <c>int</c> of 32 bits can hold). But it is possible to think
of non-sane calling conventions that would make the driver
callbacks mix up the arguments causing malfunction.
</p>
<note>
<p>
The argument type change is from signed to unsigned which
may cause problems for e.g. loop termination conditions or
error conditions if you just change the types all over the place.
</p>
</note>
</item>
<tag>Larger <c>size</c> field in <c>ErlIOVec</c></tag>
<item>
<p>
The <c>size</c> field in
<seealso marker="#ErlIOVec"><c>ErlIOVec</c></seealso>
has been changed to <c>ErlDrvSizeT</c> from <c>int</c>.
Check all code that use that field.
</p>
<p>
Automatic type casting probably makes these changes necessary only
for a driver that encounters sizes larger than 32 bits.
</p>
<note>
<p>
The <c>size</c> field changed from signed to unsigned which
may cause problems for e.g. loop termination conditions or
error conditions if you just change the types all over the place.
</p>
</note>
</item>
<tag>Arguments and return values in the driver API</tag>
<item>
<p>
Many driver API functions have changed argument type
and/or return value to <c>ErlDrvSizeT</c> from mostly <c>int</c>.
Automatic type casting probably makes these changes necessary only
for a driver that encounters sizes larger than 32 bits.
</p>
<taglist>
<tag><seealso marker="#driver_output">driver_output</seealso></tag>
<item>3rd argument</item>
<tag><seealso marker="#driver_output2">driver_output2</seealso></tag>
<item>3rd and 5th arguments</item>
<tag>
<seealso marker="#driver_output_binary">driver_output_binary</seealso>
</tag>
<item>3rd 5th and 6th arguments</item>
<tag><seealso marker="#driver_outputv">driver_outputv</seealso></tag>
<item>3rd and 5th arguments</item>
<tag>
<seealso marker="#driver_vec_to_buf">driver_vec_to_buf</seealso>
</tag>
<item>3rd argument and return value</item>
<tag><seealso marker="#driver_alloc">driver_alloc</seealso></tag>
<item>1st argument</item>
<tag><seealso marker="#driver_realloc">driver_realloc</seealso></tag>
<item>2nd argument</item>
<tag>
<seealso marker="#driver_alloc_binary">driver_alloc_binary</seealso>
</tag>
<item>1st argument</item>
<tag>
<seealso marker="#driver_realloc_binary">driver_realloc_binary</seealso>
</tag>
<item>2nd argument</item>
<tag><seealso marker="#driver_enq">driver_enq</seealso></tag>
<item>3rd argument</item>
<tag><seealso marker="#driver_pushq">driver_pushq</seealso></tag>
<item>3rd argument</item>
<tag><seealso marker="#driver_deq">driver_deq</seealso></tag>
<item>2nd argument and return value</item>
<tag><seealso marker="#driver_sizeq">driver_sizeq</seealso></tag>
<item>return value</item>
<tag><seealso marker="#driver_enq_bin">driver_enq_bin</seealso></tag>
<item>3rd and 4th argument</item>
<tag><seealso marker="#driver_pushq_bin">driver_pushq_bin</seealso></tag>
<item>3rd and 4th argument</item>
<tag><seealso marker="#driver_enqv">driver_enqv</seealso></tag>
<item>3rd argument</item>
<tag><seealso marker="#driver_pushqv">driver_pushqv</seealso></tag>
<item>3rd argument</item>
<tag><seealso marker="#driver_peekqv">driver_peekqv</seealso></tag>
<item>return value</item>
</taglist>
<note>
<p>
This is a change from signed to unsigned which
may cause problems for e.g. loop termination conditions and
error conditions if you just change the types all over the place.
</p>
</note>
</item>
</taglist>
</section>
<section>
<title>DATA TYPES</title>
<taglist>
<tag><marker id="ErlDrvSizeT"/>ErlDrvSizeT</tag>
<item><p>An unsigned integer type to be used as <c>size_t</c></p></item>
<tag><marker id="ErlDrvSSizeT"/>ErlDrvSSizeT</tag>
<item><p>A signed integer type the size of <c>ErlDrvSizeT</c></p></item>
<tag><marker id="ErlDrvSysInfo"/>ErlDrvSysInfo</tag>
<item>
<p/>
<code type="none">
typedef struct ErlDrvSysInfo {
int driver_major_version;
int driver_minor_version;
char *erts_version;
char *otp_release;
int thread_support;
int smp_support;
int async_threads;
int scheduler_threads;
int nif_major_version;
int nif_minor_version;
} ErlDrvSysInfo;
</code>
<p>
The <c>ErlDrvSysInfo</c> structure is used for storage of
information about the Erlang runtime system.
<seealso marker="#driver_system_info">driver_system_info()</seealso>
will write the system information when passed a reference to
a <c>ErlDrvSysInfo</c> structure. A description of the
fields in the structure follows:
</p>
<taglist>
<tag><c>driver_major_version</c></tag>
<item>The value of
<seealso marker="#version_management">ERL_DRV_EXTENDED_MAJOR_VERSION</seealso>
when the runtime system was compiled. This value is the same
as the value of
<seealso marker="#version_management">ERL_DRV_EXTENDED_MAJOR_VERSION</seealso>
used when compiling the driver; otherwise, the runtime system
would have refused to load the driver.
</item>
<tag><c>driver_minor_version</c></tag>
<item>The value of
<seealso marker="#version_management">ERL_DRV_EXTENDED_MINOR_VERSION</seealso>
when the runtime system was compiled. This value might differ
from the value of
<seealso marker="#version_management">ERL_DRV_EXTENDED_MINOR_VERSION</seealso>
used when compiling the driver.
</item>
<tag><c>erts_version</c></tag>
<item>A string containing the version number of the runtime system
(the same as returned by
<seealso marker="erlang#system_info_version">erlang:system_info(version)</seealso>).
</item>
<tag><c>otp_release</c></tag>
<item>A string containing the OTP release number
(the same as returned by
<seealso marker="erlang#system_info_otp_release">erlang:system_info(otp_release)</seealso>).
</item>
<tag><c>thread_support</c></tag>
<item>A value <c>!= 0</c> if the runtime system has thread support;
otherwise, <c>0</c>.
</item>
<tag><c>smp_support</c></tag>
<item>A value <c>!= 0</c> if the runtime system has SMP support;
otherwise, <c>0</c>.
</item>
<tag><c>async_threads</c></tag>
<item>The number of async threads in the async thread pool used
by <seealso marker="#driver_async">driver_async()</seealso>
(the same as returned by
<seealso marker="erlang#system_info_thread_pool_size">erlang:system_info(thread_pool_size)</seealso>).
</item>
<tag><c>scheduler_threads</c></tag>
<item>The number of scheduler threads used by the runtime system
(the same as returned by
<seealso marker="erlang#system_info_schedulers">erlang:system_info(schedulers)</seealso>).
</item>
<tag><c>nif_major_version</c></tag>
<item>The value of <c>ERL_NIF_MAJOR_VERSION</c> when the runtime system was compiled.
</item>
<tag><c>nif_minor_version</c></tag>
<item>The value of <c>ERL_NIF_MINOR_VERSION</c> when the runtime system was compiled.
</item>
</taglist>
</item>
<tag><marker id="ErlDrvBinary"/>ErlDrvBinary</tag>
<item>
<p/>
<code type="none">
typedef struct ErlDrvBinary {
ErlDrvSint orig_size;
char orig_bytes[];
} ErlDrvBinary;
</code>
<p>The <c>ErlDrvBinary</c> structure is a binary, as sent
between the emulator and the driver. All binaries are
reference counted; when <c>driver_binary_free</c> is called,
the reference count is decremented, when it reaches zero,
the binary is deallocated. The <c>orig_size</c> is the size
of the binary, and <c>orig_bytes</c> is the buffer. The
<c>ErlDrvBinary</c> does not have a fixed size, its size is
<c>orig_size + 2 * sizeof(int)</c>.</p>
<note>
<p>The <c>refc</c> field has been removed. The reference count of
an <c>ErlDrvBinary</c> is now stored elsewhere. The
reference count of an <c>ErlDrvBinary</c> can be accessed via
<seealso marker="#driver_binary_get_refc">driver_binary_get_refc()</seealso>,
<seealso marker="#driver_binary_inc_refc">driver_binary_inc_refc()</seealso>,
and
<seealso marker="#driver_binary_dec_refc">driver_binary_dec_refc()</seealso>.</p>
</note>
<p>Some driver calls, such as <c>driver_enq_binary</c>,
increment the driver reference count, and others, such as
<c>driver_deq</c> decrement it.</p>
<p>Using a driver binary instead of a normal buffer, is often
faster, since the emulator doesn't need to copy the data,
only the pointer is used.</p>
<p>A driver binary allocated in the driver, with
<c>driver_alloc_binary</c>, should be freed in the driver (unless otherwise stated),
with <c>driver_free_binary</c>. (Note that this doesn't
necessarily deallocate it, if the driver is still referred
in the emulator, the ref-count will not go to zero.)</p>
<p>Driver binaries are used in the <c>driver_output2</c> and
<c>driver_outputv</c> calls, and in the queue. Also the
driver call-back <seealso marker="driver_entry#outputv">outputv</seealso> uses driver
binaries.</p>
<p>If the driver for some reason or another, wants to keep a
driver binary around, in a static variable for instance, the
reference count should be incremented,
and the binary can later be freed in the <seealso marker="driver_entry#stop">stop</seealso> call-back, with
<c>driver_free_binary</c>.</p>
<p>Note that since a driver binary is shared by the driver and
the emulator, a binary received from the emulator or sent to
the emulator, must not be changed by the driver.</p>
<p>Since erts version 5.5 (OTP release R11B), orig_bytes is
guaranteed to be properly aligned for storage of an array of
doubles (usually 8-byte aligned).</p>
</item>
<tag>ErlDrvData</tag>
<item>
<p>The <c>ErlDrvData</c> is a handle to driver-specific data,
passed to the driver call-backs. It is a pointer, and is
most often type casted to a specific pointer in the driver.</p>
</item>
<tag>SysIOVec</tag>
<item>
<p>This is a system I/O vector, as used by <c>writev</c> on
unix and <c>WSASend</c> on Win32. It is used in
<c>ErlIOVec</c>.</p>
</item>
<tag><marker id="ErlIOVec"/>ErlIOVec</tag>
<item>
<p/>
<code type="none">
typedef struct ErlIOVec {
int vsize;
ErlDrvSizeT size;
SysIOVec* iov;
ErlDrvBinary** binv;
} ErlIOVec;
</code>
<p>The I/O vector used by the emulator and drivers, is a list
of binaries, with a <c>SysIOVec</c> pointing to the buffers
of the binaries. It is used in <c>driver_outputv</c> and the
<seealso marker="driver_entry#outputv">outputv</seealso>
driver call-back. Also, the driver queue is an
<c>ErlIOVec</c>.</p>
</item>
<tag>ErlDrvMonitor</tag>
<item>
<p>When a driver creates a monitor for a process, a
<c>ErlDrvMonitor</c> is filled in. This is an opaque
data-type which can be assigned to but not compared without
using the supplied compare function (i.e. it behaves like a struct).</p>
<p>The driver writer should provide the memory for storing the
monitor when calling <seealso marker="#driver_monitor_process">driver_monitor_process</seealso>. The
address of the data is not stored outside of the driver, so
the <c>ErlDrvMonitor</c> can be used as any other datum, it
can be copied, moved in memory, forgotten etc.</p>
</item>
<tag><marker id="ErlDrvNowData"/>ErlDrvNowData</tag>
<item>
<p>The <c>ErlDrvNowData</c> structure holds a timestamp
consisting of three values measured from some arbitrary
point in the past. The three structure members are:</p>
<taglist>
<tag>megasecs</tag>
<item>The number of whole megaseconds elapsed since the arbitrary
point in time</item>
<tag>secs</tag>
<item>The number of whole seconds elapsed since the arbitrary
point in time</item>
<tag>microsecs</tag>
<item>The number of whole microseconds elapsed since the arbitrary
point in time</item>
</taglist>
</item>
<tag><marker id="ErlDrvPDL"/>ErlDrvPDL</tag>
<item>
<p>If certain port specific data have to be accessed from other
threads than those calling the driver call-backs, a port data lock
can be used in order to synchronize the operations on the data.
Currently, the only port specific data that the emulator
associates with the port data lock is the driver queue.</p>
<p>Normally a driver instance does not have a port data lock. If
the driver instance wants to use a port data lock, it has to
create the port data lock by calling
<seealso marker="#driver_pdl_create">driver_pdl_create()</seealso>.
<em>NOTE</em>: Once the port data lock has been created, every
access to data associated with the port data lock has to be done
while having the port data lock locked. The port data lock is
locked, and unlocked, respectively, by use of
<seealso marker="#driver_pdl_lock">driver_pdl_lock()</seealso>, and
<seealso marker="#driver_pdl_unlock">driver_pdl_unlock()</seealso>.</p>
<p>A port data lock is reference counted, and when the reference
count reaches zero, it will be destroyed. The emulator will at
least increment the reference count once when the lock is
created and decrement it once when the port associated with
the lock terminates. The emulator will also increment the
reference count when an async job is enqueued and decrement
it after an async job has been invoked, or canceled. Besides
this, it is the responsibility of the driver to ensure that
the reference count does not reach zero before the last use
of the lock by the driver has been made. The reference count
can be read, incremented, and decremented, respectively, by
use of
<seealso marker="#driver_pdl_get_refc">driver_pdl_get_refc()</seealso>,
<seealso marker="#driver_pdl_inc_refc">driver_pdl_inc_refc()</seealso>, and
<seealso marker="#driver_pdl_dec_refc">driver_pdl_dec_refc()</seealso>.</p>
</item>
<tag><marker id="ErlDrvTid"/>ErlDrvTid</tag>
<item>
<p>Thread identifier.</p>
<p>See also:
<seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>,
<seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso>,
<seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>,
<seealso marker="#erl_drv_thread_self">erl_drv_thread_self()</seealso>,
and
<seealso marker="#erl_drv_equal_tids">erl_drv_equal_tids()</seealso>.
</p>
</item>
<tag><marker id="ErlDrvThreadOpts"/>ErlDrvThreadOpts</tag>
<item>
<p/>
<code type="none">
int suggested_stack_size;
</code>
<p>Thread options structure passed to
<seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
Currently the following fields exist:
</p>
<taglist>
<tag>suggested_stack_size</tag>
<item>A suggestion, in kilo-words, on how large a stack to use. A value less
than zero means default size.
</item>
</taglist>
<p>See also:
<seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>,
<seealso marker="#erl_drv_thread_opts_destroy">erl_drv_thread_opts_destroy()</seealso>,
and
<seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
</p>
</item>
<tag><marker id="ErlDrvMutex"/>ErlDrvMutex</tag>
<item>
<p>Mutual exclusion lock. Used for synchronizing access to shared data.
Only one thread at a time can lock a mutex.
</p>
<p>See also:
<seealso marker="#erl_drv_mutex_create">erl_drv_mutex_create()</seealso>,
<seealso marker="#erl_drv_mutex_destroy">erl_drv_mutex_destroy()</seealso>,
<seealso marker="#erl_drv_mutex_lock">erl_drv_mutex_lock()</seealso>,
<seealso marker="#erl_drv_mutex_trylock">erl_drv_mutex_trylock()</seealso>,
and
<seealso marker="#erl_drv_mutex_unlock">erl_drv_mutex_unlock()</seealso>.
</p>
</item>
<tag><marker id="ErlDrvCond"/>ErlDrvCond</tag>
<item>
<p>Condition variable. Used when threads need to wait for a specific
condition to appear before continuing execution. Condition variables
need to be used with associated mutexes.
</p>
<p>See also:
<seealso marker="#erl_drv_cond_create">erl_drv_cond_create()</seealso>,
<seealso marker="#erl_drv_cond_destroy">erl_drv_cond_destroy()</seealso>,
<seealso marker="#erl_drv_cond_signal">erl_drv_cond_signal()</seealso>,
<seealso marker="#erl_drv_cond_broadcast">erl_drv_cond_broadcast()</seealso>,
and
<seealso marker="#erl_drv_cond_wait">erl_drv_cond_wait()</seealso>.
</p>
</item>
<tag><marker id="ErlDrvRWLock"/>ErlDrvRWLock</tag>
<item>
<p>Read/write lock. Used to allow multiple threads to read shared data
while only allowing one thread to write the same data. Multiple threads
can read lock an rwlock at the same time, while only one thread can
read/write lock an rwlock at a time.
</p>
<p>See also:
<seealso marker="#erl_drv_rwlock_create">erl_drv_rwlock_create()</seealso>,
<seealso marker="#erl_drv_rwlock_destroy">erl_drv_rwlock_destroy()</seealso>,
<seealso marker="#erl_drv_rwlock_rlock">erl_drv_rwlock_rlock()</seealso>,
<seealso marker="#erl_drv_rwlock_tryrlock">erl_drv_rwlock_tryrlock()</seealso>,
<seealso marker="#erl_drv_rwlock_runlock">erl_drv_rwlock_runlock()</seealso>,
<seealso marker="#erl_drv_rwlock_rwlock">erl_drv_rwlock_rwlock()</seealso>,
<seealso marker="#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock()</seealso>,
and
<seealso marker="#erl_drv_rwlock_rwunlock">erl_drv_rwlock_rwunlock()</seealso>.
</p>
</item>
<tag><marker id="ErlDrvTSDKey"/>ErlDrvTSDKey</tag>
<item>
<p>Key which thread specific data can be associated with.</p>
<p>See also:
<seealso marker="#erl_drv_tsd_key_create">erl_drv_tsd_key_create()</seealso>,
<seealso marker="#erl_drv_tsd_key_destroy">erl_drv_tsd_key_destroy()</seealso>,
<seealso marker="#erl_drv_tsd_set">erl_drv_tsd_set()</seealso>,
and
<seealso marker="#erl_drv_tsd_get">erl_drv_tsd_get()</seealso>.
</p>
</item>
</taglist>
</section>
<funcs>
<func>
<name><ret>void</ret><nametext>driver_system_info(ErlDrvSysInfo *sys_info_ptr, size_t size)</nametext></name>
<fsummary>Get information about the Erlang runtime system</fsummary>
<desc>
<marker id="driver_system_info"></marker>
<p>This function will write information about the Erlang runtime
system into the
<seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso>
structure referred to by the first argument. The second
argument should be the size of the
<seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso>
structure, i.e., <c>sizeof(ErlDrvSysInfo)</c>.</p>
<p>See the documentation of the
<seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso>
structure for information about specific fields.</p>
</desc>
</func>
<func>
<name><ret>int</ret><nametext>driver_output(ErlDrvPort port, char *buf, ErlDrvSizeT len)</nametext></name>
<fsummary>Send data from driver to port owner</fsummary>
<desc>
<marker id="driver_output"></marker>
<p>The <c>driver_output</c> function is used to send data from
the driver up to the emulator. The data will be received as
terms or binary data, depending on how the driver port was
opened.</p>
<p>The data is queued in the port owner process' message
queue. Note that this does not yield to the emulator. (Since
the driver and the emulator run in the same thread.)</p>
<p>The parameter <c>buf</c> points to the data to send, and
<c>len</c> is the number of bytes.</p>
<p>The return value for all output functions is 0. (Unless the
driver is used for distribution, in which case it can fail
and return -1. For normal use, the output function always
returns 0.)</p>
</desc>
</func>
<func>
<name><ret>int</ret><nametext>driver_output2(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len)</nametext></name>
<fsummary>Send data and binary data to port owner</fsummary>
<desc>
<marker id="driver_output2"></marker>
<p>The <c>driver_output2</c> function first sends <c>hbuf</c>
(length in <c>hlen</c>) data as a list, regardless of port
settings. Then <c>buf</c> is sent as a binary or list.
E.g. if <c>hlen</c> is 3 then the port owner process will
receive <c>[H1, H2, H3 | T]</c>.</p>
<p>The point of sending data as a list header, is to facilitate
matching on the data received.</p>
<p>The return value is 0 for normal use.</p>
</desc>
</func>
<func>
<name><ret>int</ret><nametext>driver_output_binary(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, ErlDrvBinary* bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext></name>
<fsummary>Send data from a driver binary to port owner</fsummary>
<desc>
<marker id="driver_output_binary"></marker>
<p>This function sends data to port owner process from a
driver binary, it has a header buffer (<c>hbuf</c>
and <c>hlen</c>) just like <c>driver_output2</c>. The
<c>hbuf</c> parameter can be <c>NULL</c>.</p>
<p>The parameter <c>offset</c> is an offset into the binary and
<c>len</c> is the number of bytes to send.</p>
<p>Driver binaries are created with <c>driver_alloc_binary</c>.</p>
<p>The data in the header is sent as a list and the binary as
an Erlang binary in the tail of the list.</p>
<p>E.g. if <c>hlen</c> is 2, then the port owner process will
receive <c><![CDATA[[H1, H2 | <<T>>]]]></c>.</p>
<p>The return value is 0 for normal use.</p>
<p>Note that, using the binary syntax in Erlang, the driver
application can match the header directly from the binary,
so the header can be put in the binary, and hlen can be set
to 0.</p>
</desc>
</func>
<func>
<name><ret>int</ret><nametext>driver_outputv(ErlDrvPort port, char* hbuf, ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name>
<fsummary>Send vectorized data to port owner</fsummary>
<desc>
<marker id="driver_outputv"></marker>
<p>This function sends data from an IO vector, <c>ev</c>, to
the port owner process. It has a header buffer (<c>hbuf</c>
and <c>hlen</c>), just like <c>driver_output2</c>.</p>
<p>The <c>skip</c> parameter is a number of bytes to skip of
the <c>ev</c> vector from the head.</p>
<p>You get vectors of <c>ErlIOVec</c> type from the driver
queue (see below), and the <seealso marker="driver_entry#outputv">outputv</seealso> driver entry
function. You can also make them yourself, if you want to
send several <c>ErlDrvBinary</c> buffers at once. Often
it is faster to use <c>driver_output</c> or
<c>driver_output_binary</c>.</p>
<p>E.g. if <c>hlen</c> is 2 and <c>ev</c> points to an array of
three binaries, the port owner process will receive <c><![CDATA[[H1, H2, <<B1>>, <<B2>> | <<B3>>]]]></c>.</p>
<p>The return value is 0 for normal use.</p>
<p>The comment for <c>driver_output_binary</c> applies for
<c>driver_outputv</c> too.</p>
</desc>
</func>
<func>
<name><ret>ErlDrvSizeT</ret><nametext>driver_vec_to_buf(ErlIOVec *ev, char *buf, ErlDrvSizeT len)</nametext></name>
<fsummary>Collect data segments into a buffer</fsummary>
<desc>
<marker id="driver_vec_to_buf"></marker>
<p>This function collects several segments of data, referenced
by <c>ev</c>, by copying them in order to the buffer
<c>buf</c>, of the size <c>len</c>.</p>
<p>If the data is to be sent from the driver to the port owner
process, it is faster to use <c>driver_outputv</c>.</p>
<p>The return value is the space left in the buffer, i.e. if
the <c>ev</c> contains less than <c>len</c> bytes it's the
difference, and if <c>ev</c> contains <c>len</c> bytes or
more, it's 0. This is faster if there is more than one header byte,
since the binary syntax can construct integers directly from
the binary.</p>
</desc>
</func>
<func>
<name><ret>int</ret><nametext>driver_set_timer(ErlDrvPort port, unsigned long time)</nametext></name>
<fsummary>Set a timer to call the driver</fsummary>
<desc>
<marker id="driver_set_timer"></marker>
<p>This function sets a timer on the driver, which will count
down and call the driver when it is timed out. The
<c>time</c> parameter is the time in milliseconds before the
timer expires.</p>
<p>When the timer reaches 0 and expires, the driver entry
function <seealso marker="driver_entry#timeout">timeout</seealso> is called.</p>
<p>Note that there is only one timer on each driver instance;
setting a new timer will replace an older one.</p>
<p>Return value is 0 (-1 only when the <c>timeout</c> driver
function is <c>NULL</c>).</p>
</desc>
</func>
<func>
<name><ret>int</ret><nametext>driver_cancel_timer(ErlDrvPort port)</nametext></name>
<fsummary>Cancel a previously set timer</fsummary>
<desc>
<marker id="driver_cancel_timer"></marker>
<p>This function cancels a timer set with
<c>driver_set_timer</c>.</p>
<p>The return value is 0.</p>
</desc>
</func>