1 module libssh.threading;
2 
3 import core.sync.mutex;
4 import core.thread;
5 import std.algorithm.mutation;
6 
7 import libssh.c_bindings.libssh;
8 import libssh.c_bindings.callbacks;
9 import libssh.utils;
10 
11 version (LibSSHWithPThreads) {
12     void initWithPThreads() {
13         ssh_threads_set_callbacks(ssh_threads_get_pthread());
14         ssh_init();
15     }
16 }
17 
18 struct ThreadsCallbacks {
19     string type;
20     bool function(ref void* lock) mutexInit;
21     bool function(ref void* lock) mutexDestroy;
22     bool function(ref void* lock) mutexLock;
23     bool function(ref void* lock) mutexUnlock;
24     uint function() getThreadId;
25 }
26 
27 void initWithDLang() {
28     ssh_threads_set_callbacks(&dlangThreadsCallbacks);
29     ssh_init();
30 }
31 
32 void initWithCustom(ThreadsCallbacks cb) {
33     _customCallbacks = cb;
34     libsshCustomThreadsStruct.type = toStrZ(cb.type);
35     ssh_threads_set_callbacks(&libsshCustomThreadsStruct);
36     ssh_init();
37 }
38 
39 private {
40     __gshared Mutex _internalMutex;
41     __gshared Mutex[] _mutexes = [];     // To prevent GC collect mutexes sended to libssh
42 
43     extern(C) int dlangThreadsMutexInit(void** lock) {
44         auto result = new Mutex();
45 
46         synchronized (_internalMutex) {
47             _mutexes ~= result;
48         }
49 
50         *lock = cast(void*) result;
51         return 0;
52     }
53 
54     extern(C) int dlangThreadsMutexDestroy(void** lock) {
55         auto mutex = cast(Mutex) (*lock);
56 
57         synchronized (_internalMutex) {
58             _mutexes = remove!(a => a == mutex)(_mutexes);
59         }
60 
61         delete mutex;
62         *lock = null;
63 
64         return 0;
65     }
66 
67     extern(C) int dlangThreadsMutexLock(void** lock) {
68         auto mutex = cast(Mutex) (*lock);
69 
70         mutex.lock();
71 
72         return 0;
73     }
74 
75     extern(C) int dlangThreadsMutexUnlock(void** lock) {
76         auto mutex = cast(Mutex) (*lock);
77 
78         mutex.unlock();
79 
80         return 0;
81     }
82 
83     extern(C) uint dlangThreadsThreadId() {
84         auto id = Thread.getThis().id;
85 
86         // Why? Why not =) I have not better idea to cast thread id to uint from ulong.
87         // As for me - this variant is better than just remove hight part
88         return cast(uint) (((cast(ulong)(id) & 0xffffffff00000000L) >> 32) ^ (cast(uint)(id) & 0xffffffff));
89     }
90 
91     __gshared ssh_threads_callbacks_struct dlangThreadsCallbacks = {
92         type:"dlang" ~ 0,
93         mutex_init: &dlangThreadsMutexInit,
94         mutex_destroy: &dlangThreadsMutexDestroy,
95         mutex_lock: &dlangThreadsMutexLock,
96         mutex_unlock: &dlangThreadsMutexUnlock,
97         thread_id: &dlangThreadsThreadId,
98     };
99 
100     shared static this() {
101         _internalMutex = new Mutex();
102     }
103 
104 
105     __gshared ThreadsCallbacks _customCallbacks;
106 
107     extern(C) int customThreadsMutexInit(void** lock) {
108         return _customCallbacks.mutexInit(*lock) ? 0 : -1;
109     }
110     
111     extern(C) int customThreadsMutexDestroy(void** lock) {
112         return _customCallbacks.mutexDestroy(*lock) ? 0 : -1;
113     }
114     
115     extern(C) int customThreadsMutexLock(void** lock) {
116         return _customCallbacks.mutexLock(*lock) ? 0 : -1;
117     }
118     
119     extern(C) int customThreadsMutexUnlock(void** lock) {
120         return _customCallbacks.mutexUnlock(*lock) ? 0 : -1;
121     }
122     
123     extern(C) uint customThreadsGetThreadId() {
124         return _customCallbacks.getThreadId();
125     }
126 
127     __gshared ssh_threads_callbacks_struct libsshCustomThreadsStruct = {
128             type: null,
129             mutex_init: &customThreadsMutexInit,
130             mutex_destroy: &customThreadsMutexDestroy,
131             mutex_lock: &customThreadsMutexLock,
132             mutex_unlock: &customThreadsMutexUnlock,
133             thread_id: &customThreadsGetThreadId,
134     };
135 }