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 }