source: sources/src/pcExecutionThread.cc @ 1476:fdb1826f79da

Revision 1476:fdb1826f79da, 12.5 KB checked in by niam, 21 months ago (diff)

[pc::execution::thread] fix memory leak in case of detached/canceled thread

Line 
1/***************************************************************************
2 *            pcJobThreadThread.cc
3 *
4 *  Wed Oct 08 2009
5 *  Copyright  2009  Dmytro Milinevskyy
6 *  milinevskyy@gmail.com
7 ****************************************************************************/
8
9/*
10 *  This program is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU Lesser General Public License version 2.1 as published by
12 *  the Free Software Foundation;
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU Library General Public License for more details.
18 *
19 *  You should have received a copy of the GNU Lesser General Public License
20 *  along with this program; if not, write to the Free Software
21 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23
24/**
25 * vim indentation settings
26 * set tabstop=4
27 * set shiftwidth=4
28 */
29
30#include <libdodo/directives.h>
31
32#ifdef DL_EXT
33#include <dlfcn.h>
34#endif
35#ifdef PTHREAD_EXT
36#include <pthread.h>
37#endif
38#include <signal.h>
39#include <errno.h>
40#include <string.h>
41
42namespace dodo {
43    namespace exception {
44        class basic;
45    };
46
47    namespace pc {
48        namespace execution {
49            /**
50             * @struct __thread__
51             * @brief defines process information
52             */
53            struct __thread__ {
54                /**
55                 * contructor
56                 */
57                __thread__() :
58#ifdef PTHREAD_EXT
59                    thread(0),
60#endif
61                    executed(false),
62                    joined(false),
63                    status(0),
64                    ex(NULL)
65#ifdef DL_EXT
66                    ,
67                    handle(NULL)
68#endif
69                {
70#ifdef DL_EXT
71                    memset(cookie, 0x0, 32);
72#endif
73                }
74
75#ifdef PTHREAD_EXT
76                pthread_t      thread;              ///< thread descriptor
77                pthread_attr_t attr;                ///< thread attribute
78
79                /**
80                 * @return thread exit status
81                 * @param data defines user data
82                 */
83                static void *routine(void *data);
84#endif
85
86                void           *func;               ///< function to execute
87                void           *data;               ///< thread data
88
89                bool           executed;            ///< true if thread is running
90                bool           joined;              ///< true if the thread was joined
91                int            status;              ///< thread exit status
92                bool           detached;            ///< true if thread is detached
93                short          action;              ///< action on object destruction[@see onDestructionEnum]
94
95                exception::basic *ex; ///< uncought exception dodo_rethrown by thread routine
96
97#ifdef DL_EXT
98                void           *handle;             ///< handle to library
99                char            cookie[32];         ///< cookie that would be passed to deinitModule
100#endif
101            };
102        };
103    };
104};
105
106#include <libdodo/pcExecutionThread.h>
107#include <libdodo/pcExecutionJob.h>
108#include <libdodo/toolsOs.h>
109#include <libdodo/pcExecutionThreadEx.h>
110#include <libdodo/types.h>
111#include <libdodo/exceptionBasic.h>
112
113using namespace dodo::pc::execution;
114
115#ifdef PTHREAD_EXT
116void *
117__thread__::routine(void *data)
118{
119    __thread__ *ti = (__thread__ *)data;
120
121    dodo_try {
122        delete ti->ex;
123        ti->ex = NULL;
124
125        ti->status = ((execution::routine)ti->func)(ti->data);
126    } dodo_catch (exception::basic *e UNUSED) {
127        /* FIXME */
128        ti->ex = new exception::basic(*e);
129        ti->status = 0;
130    }
131
132    if (ti->detached) {
133#ifdef DL_EXT
134        if (ti->handle != NULL) {
135            thread::deinitModule deinit = (thread::deinitModule)dlsym(ti->handle, "deinitPcExecutionThreadModule");
136            if (deinit != NULL)
137                deinit(ti->cookie);
138
139#ifndef DL_FAST
140            dlclose(ti->handle);
141#endif
142        }
143#endif
144
145        delete ti->ex;
146        delete ti;
147    }
148
149    return NULL;
150}
151#endif
152
153//-------------------------------------------------------------------
154
155thread::thread(const thread &t) : job(t),
156                                  handle(new __thread__)
157{
158    *handle = *t.handle;
159}
160
161//-------------------------------------------------------------------
162
163thread::thread(routine       func,
164               void          *data,
165               short         action,
166               bool          detached,
167               unsigned long stackSize) : job(TYPE_THREAD),
168                                          handle(new __thread__)
169{
170    handle->detached = detached;
171    handle->data = data;
172    handle->func = (void *)func;
173    handle->action = action;
174
175#ifdef PTHREAD_EXT
176    pthread_attr_init(&handle->attr);
177
178    if (detached)
179        pthread_attr_setdetachstate(&handle->attr, PTHREAD_CREATE_DETACHED);
180    else
181        pthread_attr_setdetachstate(&handle->attr, PTHREAD_CREATE_JOINABLE);
182
183    errno = pthread_attr_setstacksize(&handle->attr, stackSize);
184    if (errno != 0) {
185        pthread_attr_destroy(&handle->attr);
186        delete handle;
187
188        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_CONSTRUCTOR, exception::ERRNO_ERRNO, errno, strerror(errno), __LINE__, __FILE__);
189    }
190#endif
191}
192
193#ifdef DL_EXT
194thread::thread(const dodo::string &module,
195               void             *data,
196               void             *toInit) : job(TYPE_THREAD),
197                                           handle(new __thread__)
198{
199    handle->data = data;
200
201#ifdef DL_FAST
202    handle->handle = dlopen(module.data(), RTLD_LAZY | RTLD_NODELETE);
203#else
204    handle->handle = dlopen(module.data(), RTLD_LAZY);
205#endif
206    if (handle->handle == NULL) {
207        delete handle;
208
209        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_CONSTRUCTOR, exception::ERRNO_DYNLOAD, 0, dlerror(), __LINE__, __FILE__);
210    }
211
212    initModule init = (initModule)dlsym(handle->handle, "initPcExecutionThreadModule");
213    if (init == NULL) {
214        dlclose(handle->handle);
215        delete handle;
216
217        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_CONSTRUCTOR, exception::ERRNO_DYNLOAD, 0, dlerror(), __LINE__, __FILE__);
218    }
219
220    __module__ m = init(toInit);
221
222    void *f = dlsym(handle->handle, m.hook);
223    if (f == NULL) {
224        dlclose(handle->handle);
225        delete handle;
226
227        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_CONSTRUCTOR, exception::ERRNO_DYNLOAD, 0, dlerror(), __LINE__, __FILE__);
228    }
229
230#ifdef PTHREAD_EXT
231    pthread_attr_init(&handle->attr);
232
233    if (m.detached)
234        pthread_attr_setdetachstate(&handle->attr, PTHREAD_CREATE_DETACHED);
235    else
236        pthread_attr_setdetachstate(&handle->attr, PTHREAD_CREATE_JOINABLE);
237
238    errno = pthread_attr_setstacksize(&handle->attr, m.stackSize);
239    if (errno != 0) {
240        dlclose(handle->handle);
241        pthread_attr_destroy(&handle->attr);
242        delete handle;
243
244        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_CONSTRUCTOR, exception::ERRNO_ERRNO, errno, strerror(errno), __LINE__, __FILE__);
245    }
246#endif
247
248    handle->detached = m.detached;
249    handle->action = m.action;
250    handle->func = f;
251    memcpy(handle->cookie, m.cookie, 32);
252}
253#endif
254
255//-------------------------------------------------------------------
256
257thread::~thread()
258{
259    if (!cloned) {
260#ifdef PTHREAD_EXT
261        pthread_attr_destroy(&handle->attr);
262#endif
263
264        if (isRunning()) {
265            if (handle->detached)
266                return;
267
268            switch (handle->action) {
269                case ON_DESTRUCTION_KEEP_ALIVE:
270                    handle->detached = true;
271#ifdef PTHREAD_EXT
272                    pthread_detach(handle->thread);
273#endif
274                    return;
275
276                case ON_DESTRUCTION_STOP:
277#ifdef PTHREAD_EXT
278                    pthread_cancel(handle->thread);
279#endif
280                    break;
281            }
282        }
283
284#ifdef PTHREAD_EXT
285        if (!handle->joined)
286            pthread_join(handle->thread, NULL);
287#endif
288
289#ifdef DL_EXT
290        if (handle->handle != NULL) {
291            deinitModule deinit = (deinitModule)dlsym(handle->handle, "deinitPcExecutionThreadModule");
292            if (deinit != NULL)
293                deinit(handle->cookie);
294
295#ifndef DL_FAST
296            dlclose(handle->handle);
297#endif
298        }
299#endif
300
301        delete handle->ex;
302        delete handle;
303    }
304}
305
306//-------------------------------------------------------------------
307
308void
309thread::run()
310{
311    if (isRunning())
312        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_RUN, exception::ERRNO_LIBDODO, THREADEX_ISALREADYRUNNING, PCEXECUTIONTHREADEX_ISALREADYRUNNING_STR, __LINE__, __FILE__);
313
314#ifdef PTHREAD_EXT
315    errno = pthread_create(&handle->thread, &handle->attr, __thread__::routine, handle);
316    if (errno != 0)
317        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_RUN, exception::ERRNO_ERRNO, errno, strerror(errno), __LINE__, __FILE__);
318#endif
319
320    handle->executed = true;
321}
322
323//-------------------------------------------------------------------
324
325int
326thread::wait()
327{
328    if (handle->detached) {
329        if (isRunning())
330            dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_WAIT, exception::ERRNO_LIBDODO, THREADEX_ISDETACHED, PCEXECUTIONTHREADEX_ISDETACHED_STR, __LINE__, __FILE__);
331        else
332            return handle->status;
333    }
334
335    if (handle->joined)
336        return handle->status;
337
338    if (!handle->executed)
339        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_WAIT, exception::ERRNO_LIBDODO, THREADEX_ISNOTLAUNCHED, PCEXECUTIONTHREADEX_ISNOTLAUNCHED_STR, __LINE__, __FILE__);
340
341    int status = 0;
342
343#ifdef PTHREAD_EXT
344    errno = pthread_join(handle->thread, NULL);
345    if (errno != 0)
346        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_WAIT, exception::ERRNO_ERRNO, errno, strerror(errno), __LINE__, __FILE__);
347
348    status = handle->status;
349#endif
350
351    handle->joined = true;
352    handle->executed = false;
353
354    return status;
355}
356
357//-------------------------------------------------------------------
358
359void
360thread::stop()
361{
362#ifdef PTHREAD_EXT
363    errno = pthread_cancel(handle->thread);
364    if (errno != 0)
365        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_STOP, exception::ERRNO_ERRNO, errno, strerror(errno), __LINE__, __FILE__);
366#endif
367
368    handle->executed = false;
369}
370
371//-------------------------------------------------------------------
372
373bool
374thread::isRunning() const
375{
376    if (!handle->executed)
377        return false;
378
379#ifdef PTHREAD_EXT
380    errno = pthread_kill(handle->thread, 0);
381    if (errno != 0) {
382        if (errno == ESRCH || errno == EAGAIN)
383            return false;
384
385        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_ISRUNNING, exception::ERRNO_ERRNO, errno, strerror(errno), __LINE__, __FILE__);
386    }
387#endif
388
389    return true;
390}
391
392//-------------------------------------------------------------------
393
394#ifdef DL_EXT
395thread::__module__
396thread::module(const dodo::string &module,
397               void             *toInit)
398{
399#ifdef DL_FAST
400    void *handle = dlopen(module.data(), RTLD_LAZY | RTLD_NODELETE);
401#else
402    void *handle = dlopen(module.data(), RTLD_LAZY);
403#endif
404    if (handle == NULL)
405        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_MODULE, exception::ERRNO_DYNLOAD, 0, dlerror(), __LINE__, __FILE__);
406
407    initModule init = (initModule)dlsym(handle, "initPcExecutionThreadModule");
408    if (init == NULL)
409        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_MODULE, exception::ERRNO_DYNLOAD, 0, dlerror(), __LINE__, __FILE__);
410
411    __module__ mod = init(toInit);
412
413    deinitModule deinit = (deinitModule)dlsym(handle, "deinitPcExecutionThreadModule");
414    if (deinit != NULL)
415        deinit(mod.cookie);
416
417#ifndef DL_FAST
418    if (dlclose(handle) != 0)
419        dodo_throw exception::basic(exception::MODULE_PCEXECUTIONTHREAD, THREADEX_MODULE, exception::ERRNO_DYNLOAD, 0, dlerror(), __LINE__, __FILE__);
420
421#endif
422
423    return mod;
424}
425#endif
426
427//-------------------------------------------------------------------
428
429dodo::exception::basic *
430thread::exception()
431{
432    return handle->ex;
433}
434
435//-------------------------------------------------------------------
436
437bool
438thread::self() const
439{
440    return (pthread_equal(pthread_self(), handle->thread) > 0);
441}
442
443//-------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.