carray-jit 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +146 -0
- data/README.md +11 -10
- data/carray-jit.gemspec +1 -1
- data/docs/00_Introduction.md +2 -1
- data/docs/02_KernelShapes.md +1 -1
- data/docs/03_SupportedFeatures.md +88 -11
- data/docs/04_Compiling.md +2 -2
- data/docs/05_DesignNotes.md +1 -1
- data/docs/06_Cheatsheet.md +3 -3
- data/docs/07_StepByStep.ja.md +14 -15
- data/docs/07_StepByStep.md +14 -15
- data/docs/08_LanguageReference.md +427 -0
- data/lib/carray/jit/analyzer.rb +144 -7
- data/lib/carray/jit/c_function.rb +359 -21
- data/lib/carray/jit/c_generator.rb +398 -58
- data/lib/carray/jit/compiler.rb +27 -3
- data/lib/carray/jit/expression.rb +271 -33
- data/lib/carray/jit/kernel.rb +17 -4
- data/lib/carray/jit/node.rb +15 -0
- data/lib/carray/jit/type_assignment.rb +53 -1
- data/lib/carray/jit/version.rb +1 -1
- data/lib/carray/jit.rb +7 -33
- metadata +4 -3
|
@@ -111,9 +111,10 @@ class CArray
|
|
|
111
111
|
|
|
112
112
|
def initialize (name, prototype, return_type, parameters, pointer,
|
|
113
113
|
symbol: nil,
|
|
114
|
-
block: nil, c_source: nil, origin: nil,
|
|
114
|
+
block: nil, c_source: nil, origin: nil,
|
|
115
115
|
definition: nil, helpers: nil, takes_error: false,
|
|
116
|
-
raise_messages: {}, dependencies: [], shim: nil
|
|
116
|
+
raise_messages: {}, dependencies: [], shim: nil,
|
|
117
|
+
error_state: nil)
|
|
117
118
|
# The name the declaration gave, which is what a reader wrote and
|
|
118
119
|
# what an error should say. It is nil where the declaration gave
|
|
119
120
|
# none -- `double (*)(double)` names no function.
|
|
@@ -142,9 +143,10 @@ class CArray
|
|
|
142
143
|
@takes_error = takes_error
|
|
143
144
|
@raise_messages = raise_messages
|
|
144
145
|
@origin = origin
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
|
|
146
|
+
# What the object answers when asked where this thread's flag, hook
|
|
147
|
+
# and stand-in stand. Nil when the body can neither divide by zero
|
|
148
|
+
# nor raise, and so has none of them.
|
|
149
|
+
@error_state = error_state
|
|
148
150
|
# The address of the entry point a call from Ruby takes where the
|
|
149
151
|
# signature carries a complex by value; nil where it does not, which
|
|
150
152
|
# is every other signature and every borrowed function.
|
|
@@ -324,6 +326,7 @@ class CArray
|
|
|
324
326
|
# and so never touch the flag at all.
|
|
325
327
|
outer = error_code
|
|
326
328
|
write_error(0)
|
|
329
|
+
hook = disarm_hook
|
|
327
330
|
begin
|
|
328
331
|
Access.open(arrays.map { |_, buffer, _| buffer },
|
|
329
332
|
arrays.map { |_, _, type| !type.const },
|
|
@@ -338,6 +341,7 @@ class CArray
|
|
|
338
341
|
code = error_code
|
|
339
342
|
ensure
|
|
340
343
|
write_error(outer)
|
|
344
|
+
rearm_hook(hook)
|
|
341
345
|
end
|
|
342
346
|
raise_for(code)
|
|
343
347
|
# A view was copied to be made contiguous; a writable one is copied
|
|
@@ -384,6 +388,7 @@ class CArray
|
|
|
384
388
|
}
|
|
385
389
|
outer = error_code
|
|
386
390
|
write_error(0)
|
|
391
|
+
hook = disarm_hook
|
|
387
392
|
begin
|
|
388
393
|
Access.open(arrays.map { |_, buffer, _| buffer },
|
|
389
394
|
arrays.map { |_, _, type| !type.const },
|
|
@@ -399,6 +404,7 @@ class CArray
|
|
|
399
404
|
code = error_code
|
|
400
405
|
ensure
|
|
401
406
|
write_error(outer)
|
|
407
|
+
rearm_hook(hook)
|
|
402
408
|
end
|
|
403
409
|
raise_for(code)
|
|
404
410
|
arrays.each do |array, buffer, type|
|
|
@@ -458,18 +464,28 @@ class CArray
|
|
|
458
464
|
# Windows nest, and a call made inside one leaves it armed: both borrow
|
|
459
465
|
# the flag and put it back as they found it, so an inner window answers
|
|
460
466
|
# for its own block and no other.
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
467
|
+
#
|
|
468
|
+
# `stand_in:` is the number a call answers C with once the flag stands,
|
|
469
|
+
# for a library that reads the return value as a status rather than as
|
|
470
|
+
# a value -- GSL's callbacks return `int`, and 0 is `GSL_SUCCESS`
|
|
471
|
+
# there, so a failed body reporting 0 tells GSL the step went well. It
|
|
472
|
+
# is cast to whatever the body returns and is 0 until a window says
|
|
473
|
+
# otherwise, which is what a failed call has always answered. Set for
|
|
474
|
+
# the length of the window, and put back afterwards.
|
|
475
|
+
def watching (stand_in: nil)
|
|
476
|
+
opened = open_window(nil, JIT.send(:window_stand_in, stand_in))
|
|
464
477
|
code = 0
|
|
465
478
|
begin
|
|
466
479
|
result = yield
|
|
467
|
-
code = error_code
|
|
468
480
|
rescue StandardError
|
|
469
|
-
|
|
481
|
+
# The library is complaining about what a stand-in did. The body's
|
|
482
|
+
# own failure is the cause, so it is the one that comes out.
|
|
483
|
+
code = close_window(opened)
|
|
484
|
+
opened = nil
|
|
485
|
+
raise_for(code)
|
|
470
486
|
raise
|
|
471
487
|
ensure
|
|
472
|
-
|
|
488
|
+
code = close_window(opened) if opened
|
|
473
489
|
end
|
|
474
490
|
raise_for(code)
|
|
475
491
|
result
|
|
@@ -500,17 +516,173 @@ class CArray
|
|
|
500
516
|
raise_for(error_code)
|
|
501
517
|
end
|
|
502
518
|
|
|
519
|
+
# A C function to call when the body fails, so that whoever holds the
|
|
520
|
+
# address can be told to stop rather than go on being handed stand-ins:
|
|
521
|
+
#
|
|
522
|
+
# f.on_error(nlopt["nlopt_force_stop"], opt)
|
|
523
|
+
# f.watching { nlopt_optimize.call(opt, x, minf) }
|
|
524
|
+
#
|
|
525
|
+
# `hook` is the address of a `void (*)(void *)` -- a Fiddle::Pointer,
|
|
526
|
+
# an Integer, or anything answering #to_i -- and `data` is the one
|
|
527
|
+
# argument it is called with. It is called once, from the call whose
|
|
528
|
+
# body put the flag up, and not again until the flag has been put down
|
|
529
|
+
# and has gone up again; nothing about it reaches Ruby, so it may be
|
|
530
|
+
# called from a thread that holds no GVL. `nil` takes it away.
|
|
531
|
+
#
|
|
532
|
+
# `#call` does not call it: that is one call made from Ruby, and it
|
|
533
|
+
# answers for itself by raising, as it does inside a window. A body
|
|
534
|
+
# that cannot fail has no flag to go up, so for it this is accepted
|
|
535
|
+
# and never called.
|
|
536
|
+
#
|
|
537
|
+
# Like the flag, it belongs to the thread that set it: a body lent from
|
|
538
|
+
# two threads at once has a hook and a flag in each of them, and this
|
|
539
|
+
# sets the one belonging to the thread that calls it. So it is set on
|
|
540
|
+
# the thread that will lend the address, which is where `#watching` is
|
|
541
|
+
# written anyway.
|
|
542
|
+
#
|
|
543
|
+
# Returns self.
|
|
544
|
+
def on_error (hook, data = nil)
|
|
545
|
+
state = error_state
|
|
546
|
+
return self unless state
|
|
547
|
+
address = hook.nil? ? 0 : hook.to_i
|
|
548
|
+
state.data[0, Fiddle::SIZEOF_VOIDP] =
|
|
549
|
+
[data.nil? ? 0 : data.to_i].pack("J")
|
|
550
|
+
state.hook[0, Fiddle::SIZEOF_VOIDP] = [address].pack("J")
|
|
551
|
+
state.held = hook.nil? ? nil : [hook, data]
|
|
552
|
+
self
|
|
553
|
+
end
|
|
554
|
+
|
|
503
555
|
private
|
|
504
556
|
|
|
505
|
-
#
|
|
506
|
-
#
|
|
557
|
+
# One thread's copy of the four the compiled object carries -- the
|
|
558
|
+
# flag, the hook's address, the pointer it is handed, and what a failed
|
|
559
|
+
# call answers C with -- with the Ruby objects `#on_error` was given
|
|
560
|
+
# held beside them, so that nothing the C may still call is collected.
|
|
561
|
+
ErrorState = Struct.new(:flag, :hook, :data, :standin, :held)
|
|
562
|
+
private_constant :ErrorState
|
|
563
|
+
|
|
564
|
+
# Where those copies are kept, per thread. A thread variable rather
|
|
565
|
+
# than a fiber-local one, because what is being tracked is the C's own
|
|
566
|
+
# storage: every fiber running on a thread reaches that thread's copy.
|
|
567
|
+
THREAD_STATES = :carray_jit_error_states
|
|
568
|
+
private_constant :THREAD_STATES
|
|
569
|
+
|
|
570
|
+
# This thread's, resolved by asking the object where they stand and
|
|
571
|
+
# kept for as long as the thread lives -- a thread's copies do not
|
|
572
|
+
# move. Nil where the body cannot fail at all: one that neither
|
|
573
|
+
# divides nor raises is compiled without a flag, and has no failure to
|
|
507
574
|
# report rather than an unread one.
|
|
575
|
+
def error_state
|
|
576
|
+
return nil unless @error_state
|
|
577
|
+
thread = Thread.current
|
|
578
|
+
kept = thread.thread_variable_get(THREAD_STATES)
|
|
579
|
+
unless kept
|
|
580
|
+
kept = {}
|
|
581
|
+
thread.thread_variable_set(THREAD_STATES, kept)
|
|
582
|
+
end
|
|
583
|
+
# By symbol, which carries a digest of the body: two functions with
|
|
584
|
+
# the same symbol are one compiled object and one set of copies.
|
|
585
|
+
kept[@symbol] ||= resolve_error_state
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def resolve_error_state
|
|
589
|
+
out = String.new("\0" * (4 * Fiddle::SIZEOF_VOIDP))
|
|
590
|
+
@error_state.call(out)
|
|
591
|
+
flag, hook, data, standin = out.unpack("J4")
|
|
592
|
+
ErrorState.new(Fiddle::Pointer.new(flag, 4),
|
|
593
|
+
Fiddle::Pointer.new(hook, Fiddle::SIZEOF_VOIDP),
|
|
594
|
+
Fiddle::Pointer.new(data, Fiddle::SIZEOF_VOIDP),
|
|
595
|
+
Fiddle::Pointer.new(standin, 8),
|
|
596
|
+
nil)
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
# 0 where the body cannot fail at all.
|
|
508
600
|
def error_code
|
|
509
|
-
|
|
601
|
+
state = error_state
|
|
602
|
+
state ? state.flag[0, 4].unpack1("l") : 0
|
|
510
603
|
end
|
|
511
604
|
|
|
512
605
|
def write_error (code)
|
|
513
|
-
|
|
606
|
+
state = error_state
|
|
607
|
+
state.flag[0, 4] = [code].pack("l") if state
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
# The hook's address, cleared for the length of one call from Ruby,
|
|
611
|
+
# and put back afterwards.
|
|
612
|
+
def disarm_hook
|
|
613
|
+
state = error_state
|
|
614
|
+
return nil unless state
|
|
615
|
+
held = state.hook[0, Fiddle::SIZEOF_VOIDP]
|
|
616
|
+
state.hook[0, Fiddle::SIZEOF_VOIDP] = "\0" * Fiddle::SIZEOF_VOIDP
|
|
617
|
+
held
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
def rearm_hook (held)
|
|
621
|
+
state = error_state
|
|
622
|
+
state.hook[0, Fiddle::SIZEOF_VOIDP] = held if state && held
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
# What a window does at its two ends. `#watching` is these with a
|
|
626
|
+
# block between them, and `CArray::JIT.watching` is these over several
|
|
627
|
+
# functions at once -- which is why they are named rather than written
|
|
628
|
+
# inline in the one method that had them.
|
|
629
|
+
#
|
|
630
|
+
# `hook` is the `[address, data]` a window wants a hook of its own for,
|
|
631
|
+
# and `standin` what it wants a failed call to answer C with; nil for
|
|
632
|
+
# either leaves whatever is set alone.
|
|
633
|
+
def open_window (hook = nil, standin = nil)
|
|
634
|
+
outer = error_code
|
|
635
|
+
write_error(0)
|
|
636
|
+
[outer, hook && hold_hook(*hook), standin && hold_standin(standin)]
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
# What this window's calls reported, with the flag, the hook and the
|
|
640
|
+
# stand-in put back as they were found.
|
|
641
|
+
def close_window (opened)
|
|
642
|
+
outer, held, standin = opened
|
|
643
|
+
code = error_code
|
|
644
|
+
write_error(outer)
|
|
645
|
+
restore_hook(held)
|
|
646
|
+
restore_standin(standin)
|
|
647
|
+
code
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
# This window's hook set and the one it displaced handed back, so that
|
|
651
|
+
# the window can put it there again. `#on_error` writes these two
|
|
652
|
+
# pointers; what it has no way to do is read them, and a window that
|
|
653
|
+
# brings a hook of its own has to -- otherwise it leaves with the
|
|
654
|
+
# owner's gone and nothing said about it.
|
|
655
|
+
def hold_hook (address, data)
|
|
656
|
+
state = error_state
|
|
657
|
+
return nil unless state
|
|
658
|
+
held = [state.hook[0, Fiddle::SIZEOF_VOIDP],
|
|
659
|
+
state.data[0, Fiddle::SIZEOF_VOIDP], state.held]
|
|
660
|
+
on_error(address, data)
|
|
661
|
+
held
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
def restore_hook (held)
|
|
665
|
+
state = error_state
|
|
666
|
+
return unless state && held
|
|
667
|
+
state.hook[0, Fiddle::SIZEOF_VOIDP] = held[0]
|
|
668
|
+
state.data[0, Fiddle::SIZEOF_VOIDP] = held[1]
|
|
669
|
+
state.held = held[2]
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
# This window's stand-in written, and the one it displaced handed back.
|
|
673
|
+
# Held as the eight bytes rather than as a number, so that whatever was
|
|
674
|
+
# there goes back exactly as it stood.
|
|
675
|
+
def hold_standin (value)
|
|
676
|
+
state = error_state
|
|
677
|
+
return nil unless state
|
|
678
|
+
held = state.standin[0, 8]
|
|
679
|
+
state.standin[0, 8] = [value].pack("q")
|
|
680
|
+
held
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
def restore_standin (held)
|
|
684
|
+
state = error_state
|
|
685
|
+
state.standin[0, 8] = held if state && held
|
|
514
686
|
end
|
|
515
687
|
|
|
516
688
|
def raise_for (code)
|
|
@@ -641,6 +813,14 @@ class CArray
|
|
|
641
813
|
"long double here to make one of",
|
|
642
814
|
}.freeze
|
|
643
815
|
|
|
816
|
+
# The words C builds its integer types from. They may come in any
|
|
817
|
+
# order and `int` may be left out -- `unsigned`, `long unsigned int`
|
|
818
|
+
# and `int unsigned` are all C -- while Fiddle reads only some of those
|
|
819
|
+
# orders, fails on a bare `unsigned`, and reads `int unsigned` as a
|
|
820
|
+
# signed int without saying so. So a spelling made only of these is
|
|
821
|
+
# counted here and handed to Fiddle in the one order it reads.
|
|
822
|
+
INTEGER_WORDS = %w[signed unsigned char short int long].freeze
|
|
823
|
+
|
|
644
824
|
# The CArray data type a pointer parameter takes, by the code Fiddle
|
|
645
825
|
# gave its element. This is the table CGenerator::STORAGE_C_TYPES
|
|
646
826
|
# already holds, read the other way round -- nothing new is decided
|
|
@@ -797,6 +977,7 @@ class CArray
|
|
|
797
977
|
if (reason = REFUSED[spelling])
|
|
798
978
|
raise Unsupported, "#{reason} (in `#{prototype}`)"
|
|
799
979
|
end
|
|
980
|
+
spelling = integer_spelling(spelling, text, prototype)
|
|
800
981
|
# A parameter written as an array is a pointer at the ABI, whatever
|
|
801
982
|
# its declarator says.
|
|
802
983
|
pointer ||= !array.nil?
|
|
@@ -829,6 +1010,33 @@ class CArray
|
|
|
829
1010
|
array, element, keywords.include?("const"))
|
|
830
1011
|
end
|
|
831
1012
|
|
|
1013
|
+
# `long unsigned int` as `unsigned long`, `unsigned` as `unsigned int`:
|
|
1014
|
+
# the order Fiddle reads, for a spelling made only of INTEGER_WORDS.
|
|
1015
|
+
# Anything else is handed back untouched. A combination C does not
|
|
1016
|
+
# have -- `signed unsigned`, `short long`, three `long`s -- is refused
|
|
1017
|
+
# here rather than left to a parser that has been seen to guess.
|
|
1018
|
+
def integer_spelling (spelling, text, prototype)
|
|
1019
|
+
words = spelling.split(/\s+/)
|
|
1020
|
+
return spelling unless words.all? { |word| INTEGER_WORDS.include?(word) }
|
|
1021
|
+
count = words.tally
|
|
1022
|
+
longs = count.fetch("long", 0)
|
|
1023
|
+
base = count.keys & %w[char short]
|
|
1024
|
+
if count.values_at("signed", "unsigned", "char", "short", "int")
|
|
1025
|
+
.any? { |n| n.to_i > 1 } ||
|
|
1026
|
+
(count.key?("signed") && count.key?("unsigned")) ||
|
|
1027
|
+
base.size > 1 || (base.any? && longs > 0) || longs > 2 ||
|
|
1028
|
+
(count.key?("char") && count.key?("int"))
|
|
1029
|
+
raise Unsupported,
|
|
1030
|
+
"`#{text.strip}` in `#{prototype}` is not a C integer type"
|
|
1031
|
+
end
|
|
1032
|
+
sign = count.key?("unsigned") ? "unsigned " : ""
|
|
1033
|
+
width = base.first || { 0 => "int", 1 => "long", 2 => "long long" }.fetch(longs)
|
|
1034
|
+
# `signed char` is a type of its own in C, apart from plain `char`,
|
|
1035
|
+
# and Fiddle reads both as the one byte they are.
|
|
1036
|
+
sign = "signed " if width == "char" && count.key?("signed")
|
|
1037
|
+
"#{sign}#{width}"
|
|
1038
|
+
end
|
|
1039
|
+
|
|
832
1040
|
# The code for a complex spelling, or nil for anything else. `float
|
|
833
1041
|
# _Complex`, `complex float`, `float complex` -- the word may sit on
|
|
834
1042
|
# either side, as C allows.
|
|
@@ -980,8 +1188,127 @@ class CArray
|
|
|
980
1188
|
declared_parameters)
|
|
981
1189
|
end
|
|
982
1190
|
|
|
1191
|
+
# One window over several functions, for a call that is handed more
|
|
1192
|
+
# than one address:
|
|
1193
|
+
#
|
|
1194
|
+
# CArray::JIT.watching(objective, gradient) do
|
|
1195
|
+
# NLopt.optimize(option, x, minimum)
|
|
1196
|
+
# end
|
|
1197
|
+
#
|
|
1198
|
+
# Every flag is put down before the block, and every one is read after
|
|
1199
|
+
# it -- because a flag nobody reads is a failure that becomes a wrong
|
|
1200
|
+
# answer, and then surfaces on an unrelated later window. Whichever
|
|
1201
|
+
# failure comes first in the argument list is the one raised, and a
|
|
1202
|
+
# failure raised this way outranks whatever the block raised, for the
|
|
1203
|
+
# reason `CFunction#watching` gives: the library is complaining about
|
|
1204
|
+
# a stand-in, and the stand-in is the consequence.
|
|
1205
|
+
#
|
|
1206
|
+
# `on_error:` is the `[hook, data]` pair `CFunction#on_error` takes,
|
|
1207
|
+
# set on every function for the length of the window and taken off
|
|
1208
|
+
# again afterwards -- with whatever hook was there before put back.
|
|
1209
|
+
# That is the difference from calling `#on_error` around the block:
|
|
1210
|
+
# the pair can be read back here and cannot be read back from Ruby, so
|
|
1211
|
+
# a window that brings its own hook is the only way to borrow one
|
|
1212
|
+
# without leaving the owner's gone.
|
|
1213
|
+
#
|
|
1214
|
+
# `stand_in:` is the number a failed call answers C with, as
|
|
1215
|
+
# `CFunction#watching` takes it, and is set on every function watched.
|
|
1216
|
+
# Which number is right belongs to the window and not to the body: the
|
|
1217
|
+
# same `int (*)(...)` may be handed to a routine that stops on a
|
|
1218
|
+
# non-zero status and to one that reads it as a rejected step and tries
|
|
1219
|
+
# again, and the body has no way of knowing which it was given to.
|
|
1220
|
+
#
|
|
1221
|
+
# `nil` in the list is skipped, so an optional gradient or jacobian may
|
|
1222
|
+
# be passed as it stands, and a function named twice is watched once.
|
|
1223
|
+
# A body that cannot fail has no flag, no hook and no stand-in, and
|
|
1224
|
+
# passing one changes nothing -- which is what lets a binding watch
|
|
1225
|
+
# what it was given without asking which kind of body it is.
|
|
1226
|
+
def watching (*functions, on_error: nil, stand_in: nil)
|
|
1227
|
+
armed = functions.compact
|
|
1228
|
+
armed.each do |function|
|
|
1229
|
+
next if function.is_a?(CFunction)
|
|
1230
|
+
raise TypeError,
|
|
1231
|
+
"`CArray::JIT.watching` watches compiled functions, and " \
|
|
1232
|
+
"#{function.class} is not one -- pass what `jit_function` " \
|
|
1233
|
+
"or `jit_extern` answered"
|
|
1234
|
+
end
|
|
1235
|
+
armed = armed.uniq
|
|
1236
|
+
hook = window_hook(on_error)
|
|
1237
|
+
standin = window_stand_in(stand_in)
|
|
1238
|
+
return yield if armed.empty?
|
|
1239
|
+
opened = armed.map { |function|
|
|
1240
|
+
function.send(:open_window, hook, standin)
|
|
1241
|
+
}
|
|
1242
|
+
codes = []
|
|
1243
|
+
begin
|
|
1244
|
+
result = yield
|
|
1245
|
+
rescue StandardError
|
|
1246
|
+
# Closed here rather than in the ensure below, so that the failure
|
|
1247
|
+
# raised next is raised from inside this rescue and carries the
|
|
1248
|
+
# library's own complaint as its cause.
|
|
1249
|
+
codes = close_windows(armed, opened)
|
|
1250
|
+
opened = nil
|
|
1251
|
+
raise_first_failure(armed, codes)
|
|
1252
|
+
raise
|
|
1253
|
+
ensure
|
|
1254
|
+
codes = close_windows(armed, opened) if opened
|
|
1255
|
+
end
|
|
1256
|
+
raise_first_failure(armed, codes)
|
|
1257
|
+
result
|
|
1258
|
+
end
|
|
1259
|
+
|
|
983
1260
|
private
|
|
984
1261
|
|
|
1262
|
+
# Every window closed, in the order they were opened, and what each of
|
|
1263
|
+
# them saw. All of them, whatever happened in the block: a flag nobody
|
|
1264
|
+
# reads is one that surfaces on an unrelated later window.
|
|
1265
|
+
def close_windows (armed, opened)
|
|
1266
|
+
armed.zip(opened).map { |function, state|
|
|
1267
|
+
function.send(:close_window, state)
|
|
1268
|
+
}
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
# The first failure among the flags just read, raised as the function
|
|
1272
|
+
# that reported it would raise it. A code of 0 is no failure and is
|
|
1273
|
+
# passed over, so this walks the list in the order it was written and
|
|
1274
|
+
# stops at the first that has something to say.
|
|
1275
|
+
def raise_first_failure (armed, codes)
|
|
1276
|
+
armed.zip(codes).each do |function, code|
|
|
1277
|
+
function.send(:raise_for, code)
|
|
1278
|
+
end
|
|
1279
|
+
end
|
|
1280
|
+
|
|
1281
|
+
# The number a window hands the C as a failed call's answer. An
|
|
1282
|
+
# Integer because that is what the object carries it in -- one 64-bit
|
|
1283
|
+
# word, cast at the return -- and because a status is a whole number
|
|
1284
|
+
# wherever one is read.
|
|
1285
|
+
def window_stand_in (stand_in)
|
|
1286
|
+
return nil if stand_in.nil?
|
|
1287
|
+
unless stand_in.is_a?(Integer)
|
|
1288
|
+
raise TypeError,
|
|
1289
|
+
"`stand_in:` is the number a failed call answers C with, " \
|
|
1290
|
+
"and #{stand_in.class} is not one -- it is cast to whatever " \
|
|
1291
|
+
"the body returns, so what is given here is an Integer"
|
|
1292
|
+
end
|
|
1293
|
+
unless (-(2**63)...2**63).cover?(stand_in)
|
|
1294
|
+
raise RangeError,
|
|
1295
|
+
"`stand_in:` is carried in one 64-bit word, and " \
|
|
1296
|
+
"#{stand_in} does not fit in one"
|
|
1297
|
+
end
|
|
1298
|
+
stand_in
|
|
1299
|
+
end
|
|
1300
|
+
|
|
1301
|
+
def window_hook (on_error)
|
|
1302
|
+
return nil if on_error.nil?
|
|
1303
|
+
unless on_error.is_a?(Array) && (1..2).cover?(on_error.size)
|
|
1304
|
+
raise ArgumentError,
|
|
1305
|
+
"`on_error:` takes the hook and the one pointer it is " \
|
|
1306
|
+
"handed, as `CFunction#on_error` does -- " \
|
|
1307
|
+
"`on_error: [handle[\"nlopt_force_stop\"], option]`"
|
|
1308
|
+
end
|
|
1309
|
+
[on_error[0], on_error[1]]
|
|
1310
|
+
end
|
|
1311
|
+
|
|
985
1312
|
def bind_c_function (prototype, name, return_type, parameters, from)
|
|
986
1313
|
handle = library_handle(from)
|
|
987
1314
|
begin
|
|
@@ -1154,10 +1481,12 @@ class CArray
|
|
|
1154
1481
|
raise Unsupported,
|
|
1155
1482
|
(if type.indexable?
|
|
1156
1483
|
"`#{loose}` is declared `#{type.text}`, which is a pointer; " \
|
|
1157
|
-
"index it, as in `#{loose}[0]
|
|
1484
|
+
"index it, as in `#{loose}[0]`, or ask whether one came, " \
|
|
1485
|
+
"as in `if #{loose}` or `#{loose}.nil?`"
|
|
1158
1486
|
else
|
|
1159
1487
|
"`#{loose}` is declared `#{type.text}`, which is a slot in " \
|
|
1160
|
-
"the signature rather than a value; the body cannot read
|
|
1488
|
+
"the signature rather than a value; the body cannot read " \
|
|
1489
|
+
"it, only ask whether it was filled, as in `#{loose}.nil?`"
|
|
1161
1490
|
end)
|
|
1162
1491
|
end
|
|
1163
1492
|
# A parameter of any computation type the body can work in, uint64
|
|
@@ -1193,9 +1522,18 @@ class CArray
|
|
|
1193
1522
|
# Where the body can divide by zero or reach outside an array, the
|
|
1194
1523
|
# object carries a place to say so. Reading it is what lets a call
|
|
1195
1524
|
# from Ruby raise what the same expression raises in Ruby.
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1525
|
+
#
|
|
1526
|
+
# The place is the calling thread's, and a thread-local is not a name
|
|
1527
|
+
# the loader can hand an address for, so what is taken here is the
|
|
1528
|
+
# accessor beside it -- called once per thread, from that thread. It
|
|
1529
|
+
# is named after the body, the four it answers for being `static`, so
|
|
1530
|
+
# that two objects generated this way can be linked side by side.
|
|
1531
|
+
error_state = if generator.uses_error_flag?
|
|
1532
|
+
accessor = CGenerator.error_state_name(symbol)
|
|
1533
|
+
Fiddle::Function.new(handle[accessor],
|
|
1534
|
+
[Fiddle::TYPE_VOIDP],
|
|
1535
|
+
Fiddle::TYPE_VOID, name: accessor)
|
|
1536
|
+
end
|
|
1199
1537
|
# A body that reports failures is generated a second time for pasting,
|
|
1200
1538
|
# with the flag as a parameter. A second generator rather than the
|
|
1201
1539
|
# same one twice: what it emitted is what it holds, and the two forms
|
|
@@ -1214,7 +1552,7 @@ class CArray
|
|
|
1214
1552
|
symbol: symbol,
|
|
1215
1553
|
shim: shim_symbol && handle[shim_symbol],
|
|
1216
1554
|
block: block, c_source: generator.provenance + c_source,
|
|
1217
|
-
origin: origin,
|
|
1555
|
+
origin: origin, error_state: error_state,
|
|
1218
1556
|
definition: pasted.function_definition,
|
|
1219
1557
|
helpers: pasted.helper_needs,
|
|
1220
1558
|
dependencies: called.values,
|