Mercurial > projects > ldc
comparison tango/tango/sys/SharedLib.d @ 132:1700239cab2e trunk
[svn r136] MAJOR UNSTABLE UPDATE!!!
Initial commit after moving to Tango instead of Phobos.
Lots of bugfixes...
This build is not suitable for most things.
author | lindquist |
---|---|
date | Fri, 11 Jan 2008 17:57:40 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
131:5825d48b27d1 | 132:1700239cab2e |
---|---|
1 /** | |
2 * The shared library module provides a basic layer around the native functions | |
3 * used to load symbols from shared libraries. | |
4 * | |
5 * Copyright: Copyright (C) 2007 Tomasz Stachowiak | |
6 * License: BSD style: $(LICENSE) | |
7 * Authors: Tomasz Stachowiak, Anders Bergh | |
8 */ | |
9 | |
10 module tango.sys.SharedLib; | |
11 | |
12 | |
13 private { | |
14 import tango.core.Exception : TracedException; | |
15 import tango.stdc.stringz : fromUtf8z; | |
16 | |
17 version (Windows) { | |
18 import tango.sys.Common : SysError; | |
19 | |
20 alias void* HINSTANCE, HMODULE; | |
21 alias int BOOL; | |
22 | |
23 extern (Windows) { | |
24 void* GetProcAddress(HINSTANCE, char*); | |
25 HINSTANCE LoadLibraryA(char*); | |
26 BOOL FreeLibrary(HMODULE); | |
27 } | |
28 } | |
29 else version (Posix) { | |
30 import tango.stdc.posix.dlfcn; | |
31 } | |
32 else { | |
33 static assert (false, "No support for this platform"); | |
34 } | |
35 | |
36 version (SharedLibVerbose) import tango.util.log.Trace; | |
37 } | |
38 | |
39 | |
40 /** | |
41 SharedLib is an interface to system-specific shared libraries, such | |
42 as ".dll", ".so" or ".dylib" files. It provides a simple interface to obtain | |
43 symbol addresses (such as function pointers) from these libraries. | |
44 | |
45 Example: | |
46 ---- | |
47 | |
48 void main() { | |
49 if (auto lib = SharedLib.load(`c:\windows\system32\opengl32.dll`)) { | |
50 Trace.formatln("Library successfully loaded"); | |
51 | |
52 void* ptr = lib.getSymbol("glClear"); | |
53 if (ptr) { | |
54 Trace.formatln("Symbol glClear found. Address = 0x{:x}", ptr); | |
55 } else { | |
56 Trace.formatln("Symbol glClear not found"); | |
57 } | |
58 | |
59 lib.unload(); | |
60 } else { | |
61 Trace.formatln("Could not load the library"); | |
62 } | |
63 | |
64 assert (0 == SharedLib.numLoadedLibs); | |
65 } | |
66 | |
67 ---- | |
68 | |
69 This implementation uses reference counting, thus a library is not loaded | |
70 again if it has been loaded before and not unloaded by the user. | |
71 Unloading a SharedLib decreases its reference count. When it reaches 0, | |
72 the shared library associated with it is unloaded and the SharedLib instance | |
73 is deleted. Please do not delete SharedLib instances manually, unload() will | |
74 take care of it. | |
75 | |
76 Note: | |
77 SharedLib is thread-safe. | |
78 */ | |
79 final class SharedLib { | |
80 /// Mapped from RTLD_NOW, RTLD_LAZY, RTLD_GLOBAL and RTLD_LOCAL | |
81 enum LoadMode { | |
82 Now = 0b1, | |
83 Lazy = 0b10, | |
84 Global = 0b100, | |
85 Local = 0b1000 | |
86 } | |
87 | |
88 | |
89 /** | |
90 Loads an OS-specific shared library. | |
91 | |
92 Note: | |
93 Please use this function instead of the constructor, which is private. | |
94 | |
95 Params: | |
96 path = The path to a shared library to be loaded | |
97 mode = Library loading mode. See LoadMode | |
98 | |
99 Returns: | |
100 A SharedLib instance being a handle to the library, or null if it | |
101 could not be loaded and SharedLib.throwExceptions is set to false. | |
102 Otherwise, throws SharedLibException | |
103 */ | |
104 static SharedLib load(char[] path, LoadMode mode = LoadMode.Now | LoadMode.Global) { | |
105 SharedLib res; | |
106 | |
107 synchronized (mutex) { | |
108 auto lib = path in loadedLibs; | |
109 if (lib) { | |
110 version (SharedLibVerbose) Trace.formatln("SharedLib found in the hashmap"); | |
111 res = *lib; | |
112 } | |
113 else { | |
114 version (SharedLibVerbose) Trace.formatln("Creating a new instance of SharedLib"); | |
115 res = new SharedLib(path); | |
116 loadedLibs[path] = res; | |
117 } | |
118 | |
119 ++res.refCnt; | |
120 } | |
121 | |
122 bool delRes = false; | |
123 Exception exc; | |
124 | |
125 synchronized (res) { | |
126 if (!res.loaded) { | |
127 version (SharedLibVerbose) Trace.formatln("Loading the SharedLib"); | |
128 try { | |
129 res.load_(mode); | |
130 } catch (Exception e) { | |
131 exc = e; | |
132 } | |
133 } | |
134 | |
135 if (res.loaded) { | |
136 version (SharedLibVerbose) Trace.formatln("SharedLib successfully loaded, returning"); | |
137 return res; | |
138 } else { | |
139 synchronized (mutex) { | |
140 if (path in loadedLibs) { | |
141 version (SharedLibVerbose) Trace.formatln("Removing the SharedLib from the hashmap"); | |
142 loadedLibs.remove(path); | |
143 } | |
144 } | |
145 } | |
146 | |
147 // make sure that only one thread will delete the object | |
148 if (0 == --res.refCnt) { | |
149 delRes = true; | |
150 } | |
151 } | |
152 | |
153 if (delRes) { | |
154 version (SharedLibVerbose) Trace.formatln("Deleting the SharedLib"); | |
155 delete res; | |
156 } | |
157 | |
158 if (exc !is null) { | |
159 throw exc; | |
160 } | |
161 | |
162 version (SharedLibVerbose) Trace.formatln("SharedLib not loaded, returning null"); | |
163 return null; | |
164 } | |
165 | |
166 | |
167 /** | |
168 Unloads the OS-specific shared library associated with this SharedLib instance. | |
169 | |
170 Note: | |
171 It's invalid to use the object after unload() has been called, as unload() | |
172 will delete it if it's not referenced any more. | |
173 | |
174 Throws SharedLibException on failure. In this case, the SharedLib object is not deleted. | |
175 */ | |
176 void unload() { | |
177 bool deleteThis = false; | |
178 | |
179 synchronized (this) { | |
180 assert (loaded); | |
181 assert (refCnt > 0); | |
182 | |
183 synchronized (mutex) { | |
184 if (--refCnt <= 0) { | |
185 version (SharedLibVerbose) Trace.formatln("Unloading the SharedLib"); | |
186 try { | |
187 unload_(); | |
188 } catch (Exception e) { | |
189 ++refCnt; | |
190 throw e; | |
191 } | |
192 | |
193 assert ((path in loadedLibs) !is null); | |
194 loadedLibs.remove(path); | |
195 | |
196 deleteThis = true; | |
197 } | |
198 } | |
199 } | |
200 if (deleteThis) { | |
201 version (SharedLibVerbose) Trace.formatln("Deleting the SharedLib"); | |
202 delete this; | |
203 } | |
204 } | |
205 | |
206 | |
207 /** | |
208 Returns the path to the OS-specific shared library associated with this object. | |
209 */ | |
210 char[] path() { | |
211 return this.path_; | |
212 } | |
213 | |
214 | |
215 /** | |
216 Obtains the address of a symbol within the shared library | |
217 | |
218 Params: | |
219 name = The name of the symbol; must be a null-terminated C string | |
220 | |
221 Returns: | |
222 A pointer to the symbol or null if it's not present in the library | |
223 and SharedLib.throwExceptions is set to false. Otherwise, throws SharedLibException | |
224 */ | |
225 void* getSymbol(char* name) { | |
226 assert (loaded); | |
227 return getSymbol_(name); | |
228 } | |
229 | |
230 | |
231 | |
232 /** | |
233 Returns the total number of libraries currently loaded by SharedLib | |
234 */ | |
235 static uint numLoadedLibs() { | |
236 return loadedLibs.keys.length; | |
237 } | |
238 | |
239 | |
240 private { | |
241 version (Windows) { | |
242 HMODULE handle; | |
243 | |
244 void load_(LoadMode mode) { | |
245 handle = LoadLibraryA((this.path_ ~ \0).ptr); | |
246 if (handle is null && SharedLib.throwExceptions) { | |
247 throw new SharedLibException("Couldn't load shared library '" ~ this.path_ ~ "' : " ~ SysError.lastMsg); | |
248 } | |
249 } | |
250 | |
251 void* getSymbol_(char* name) { | |
252 auto res = GetProcAddress(handle, name); | |
253 if (res is null && SharedLib.throwExceptions) { | |
254 throw new SharedLibException("Couldn't load symbol '" ~ fromUtf8z(name) ~ "' from shared library '" ~ this.path_ ~ "' : " ~ SysError.lastMsg); | |
255 } else { | |
256 return res; | |
257 } | |
258 } | |
259 | |
260 void unload_() { | |
261 if (0 == FreeLibrary(handle) && SharedLib.throwExceptions) { | |
262 throw new SharedLibException("Couldn't unload shared library '" ~ this.path_ ~ "' : " ~ SysError.lastMsg); | |
263 } | |
264 } | |
265 } | |
266 else version (Posix) { | |
267 void* handle; | |
268 | |
269 void load_(LoadMode mode) { | |
270 int mode_; | |
271 if (mode & LoadMode.Now) mode_ |= RTLD_NOW; | |
272 if (mode & LoadMode.Lazy) mode_ |= RTLD_LAZY; | |
273 if (mode & LoadMode.Global) mode_ |= RTLD_GLOBAL; | |
274 if (mode & LoadMode.Local) mode_ |= RTLD_LOCAL; | |
275 | |
276 handle = dlopen((this.path_ ~ \0).ptr, mode_); | |
277 if (handle is null && SharedLib.throwExceptions) { | |
278 throw new SharedLibException("Couldn't load shared library: " ~ fromUtf8z(dlerror())); | |
279 } | |
280 } | |
281 | |
282 void* getSymbol_(char* name) { | |
283 auto res = dlsym(handle, name); | |
284 if (res is null && SharedLib.throwExceptions) { | |
285 throw new SharedLibException("Couldn't load symbol: " ~ fromUtf8z(dlerror())); | |
286 } else { | |
287 return res; | |
288 } | |
289 } | |
290 | |
291 void unload_() { | |
292 if (0 != dlclose(handle) && SharedLib.throwExceptions) { | |
293 throw new SharedLibException("Couldn't unload shared library: " ~ fromUtf8z(dlerror())); | |
294 } | |
295 } | |
296 } | |
297 else { | |
298 static assert (false, "No support for this platform"); | |
299 } | |
300 | |
301 | |
302 char[] path_; | |
303 int refCnt = 0; | |
304 | |
305 | |
306 bool loaded() { | |
307 return handle !is null; | |
308 } | |
309 | |
310 | |
311 this(char[] path) { | |
312 this.path_ = path.dup; | |
313 } | |
314 } | |
315 | |
316 | |
317 private static { | |
318 SharedLib[char[]] loadedLibs; | |
319 Object mutex; | |
320 } | |
321 | |
322 | |
323 static this() { | |
324 mutex = new Object; | |
325 } | |
326 | |
327 | |
328 /// Set this to false if you don't want SharedLib to throw exceptions in symbol(), load() and unload() | |
329 static bool throwExceptions = true; | |
330 } | |
331 | |
332 | |
333 class SharedLibException : TracedException { | |
334 this (char[] msg) { | |
335 super(msg); | |
336 } | |
337 } |