1require 'dl'
2require 'thread'
3
4module DL
5	# The mutual exclusion (Mutex) semaphore for the DL module
6  SEM = Mutex.new # :nodoc:
7
8  if DL.fiddle?
9    # A Hash of callback Procs
10    #
11    # Uses Fiddle
12    CdeclCallbackProcs = {} # :nodoc:
13
14    # A Hash of the addresses of callback Proc
15    #
16    # Uses Fiddle
17    CdeclCallbackAddrs = {} # :nodoc:
18
19    # A Hash of Stdcall callback Procs
20    #
21    # Uses Fiddle on win32
22    StdcallCallbackProcs = {} # :nodoc:
23
24    # A Hash of the addresses of Stdcall callback Procs
25    #
26    # Uses Fiddle on win32
27    StdcallCallbackAddrs = {} # :nodoc:
28  end
29
30  def set_callback_internal(proc_entry, addr_entry, argc, ty, abi = nil, &cbp)
31    if( argc < 0 )
32      raise(ArgumentError, "arity should not be less than 0.")
33    end
34    addr = nil
35
36    if DL.fiddle?
37      abi ||= Fiddle::Function::DEFAULT
38      closure = Fiddle::Closure::BlockCaller.new(ty, [TYPE_VOIDP] * argc, abi, &cbp)
39      proc_entry[closure.to_i] = closure
40      addr = closure.to_i
41    else
42      SEM.synchronize{
43        ary = proc_entry[ty]
44        (0...MAX_CALLBACK).each{|n|
45          idx = (n * DLSTACK_SIZE) + argc
46          if( ary[idx].nil? )
47            ary[idx] = cbp
48            addr = addr_entry[ty][idx]
49            break
50          end
51        }
52      }
53    end
54
55    addr
56  end
57
58  def set_cdecl_callback(ty, argc, &cbp)
59    set_callback_internal(CdeclCallbackProcs, CdeclCallbackAddrs, argc, ty, &cbp)
60  end
61
62  def set_stdcall_callback(ty, argc, &cbp)
63    if DL.fiddle?
64      set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, Fiddle::Function::STDCALL, &cbp)
65    else
66      set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, &cbp)
67    end
68  end
69
70  def remove_callback_internal(proc_entry, addr_entry, addr, ctype = nil)
71    if DL.fiddle?
72      addr = addr.to_i
73      return false unless proc_entry.key?(addr)
74      proc_entry.delete(addr)
75      true
76    else
77      index = nil
78      if( ctype )
79        addr_entry[ctype].each_with_index{|xaddr, idx|
80          if( xaddr == addr )
81            index = idx
82          end
83        }
84      else
85        addr_entry.each{|ty,entry|
86          entry.each_with_index{|xaddr, idx|
87            if( xaddr == addr )
88              index = idx
89            end
90          }
91        }
92      end
93      if( index and proc_entry[ctype][index] )
94        proc_entry[ctype][index] = nil
95        return true
96      else
97        return false
98      end
99    end
100  end
101
102  def remove_cdecl_callback(addr, ctype = nil)
103    remove_callback_internal(CdeclCallbackProcs, CdeclCallbackAddrs, addr, ctype)
104  end
105
106  def remove_stdcall_callback(addr, ctype = nil)
107    remove_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, addr, ctype)
108  end
109
110  alias set_callback set_cdecl_callback
111  alias remove_callback remove_cdecl_callback
112end
113