root/threadman/trunk/threadman.h

Revision 662, 7.2 kB (checked in by tapted, 2 years ago)

Make test cases and fix memory leaks in Runner goo

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision url Rev Revision
Line 
1/* $Id$ $URL$ */
2#ifndef THREADMAN_DOT_AITCH
3#define THREADMAN_DOT_AITCH
4
5/**\file threadman.h
6 * Declaration of ThreadMan
7 * \author Trent Apted <tapted@it.usyd.edu.au>
8 * $Rev$
9 * $Date$
10 */
11
12#include <vector>
13
14//forward decs
15class SyncThreads;
16struct SDL_Thread;
17struct SDL_mutex;
18
19/** Singleton Manager for SDL Threads that we want to detach */
20class ThreadMan {
21public:
22    /** Thread priorities. Note we do not support high priority -- it
23     * needs root perms on Linux and screws around with Windows' crappy
24     * round robin process scheduler.
25     * Note also that on Windows, these are relative to the parent while
26     * on POSIX they are absolute.
27     */
28    enum PRIORITY {
29        INHERIT,      ///< Inherit from parent process
30        NORMAL,       ///< Normal or nice(0)
31        BELOW_NORMAL, ///< Below Normal or nice(5)
32        LOW,          ///< Low or nice (10)
33        IDLE          ///< Idle of nice(19)
34    };
35    typedef int (*THREAD)(void*); ///< Type for threads
36    struct Runner; //forward dec
37
38    ///\name Member Functions for running threads
39    //@{
40    /**
41     * Run a thread and forget about it, to be cleaned on the next call to run(), if it has finished.
42     *
43     * \param fn The thread to run (like that passed to SDL_CreateThread)
44     * \param data The data to pass to the thread
45     * \param sync If true, use synchronised mode. In synchronised mode, only one thread exists for each distinct
46     *             \a fn and we reuse it when each call finishes, rather than cleaning up. stopSync() will stop
47     *             all threads started in this fashion (waiting for \a fn to finish if it is currently running).
48     * \param pri The thread priority
49     * \param thr if non-null this is assigned to the SDL_Thread that will (eventually) run the function
50     */
51    bool run(THREAD fn, void* data = 0, bool sync = false, PRIORITY pri = INHERIT, SDL_Thread **thr = 0);
52    bool run(Runner *fn, void* data = 0, bool sync = false, PRIORITY pri = INHERIT, SDL_Thread **thr = 0);
53    //@}
54
55    /** Stop all synchronised threads (does not block) */
56    void stopSync();
57    /** Wait for all synchronised threads to finish (may block) */
58    void joinSync();
59    /** Stop asynchronous threads, wait for them to finish (may block) */
60    void stopAsync();
61
62    /** Return a reference to the singleton, creating it if it doesn't exist yet */
63    static ThreadMan& tman();
64
65    /**\name Static version of run() */
66    //@{
67    static bool srun(THREAD fn, void* data = 0, bool sync = false, PRIORITY pri = INHERIT, SDL_Thread **thr = 0) {
68        return tman().run(fn, data, sync, pri, thr);
69    }
70    static bool srun(Runner *fn, void* data = 0, bool sync = false, PRIORITY pri = INHERIT, SDL_Thread **thr = 0) {
71        return tman().run(fn, data, sync, pri, thr);
72    }
73    //@}
74
75    /** Set the priority of *this* thread */
76    static void setMyPriority(PRIORITY pri);
77    /** Sleep this thread for X ms */
78    static void sleep(unsigned ms);
79    /** Get the thread ID */
80    static int thread_id();
81
82    ///\name Runner classes, for more flexible thread starting -- work in progress
83    //@{
84    /** Superclass runner */
85    struct Runner {
86        bool cleanup;
87        explicit Runner(bool clean) : cleanup(clean) {}
88        virtual int run(void* data) = 0;
89        virtual bool operator==(const Runner &rhs) const = 0;
90        virtual ~Runner() {}
91    };
92    /** Functor runner, taking any single pointer argument, returning something castable to int */
93    template <class F, class Arg = void>
94        struct F1Runner : public Runner {
95            F f;
96            F1Runner(const F& ff, bool clean = false) : Runner(clean), f(ff) {}
97            virtual int run(void* data) {return static_cast<int>(f(static_cast<Arg*>(data)));}
98            virtual bool operator==(const Runner &rhs) const {
99                const F1Runner *r = dynamic_cast<const F1Runner*>(&rhs);
100                return r && r->f == f;
101            }
102        };
103    /** Functor runner, taking a no argument, returning something castable to int */
104    template <class F>
105        struct F0Runner : public Runner {
106            F f;
107            F0Runner(const F& ff, bool clean = false) : Runner(clean), f(ff) {}
108            virtual int run(void* data) {return static_cast<int>(f());}
109            virtual bool operator==(const Runner &rhs) const {
110                const F0Runner *r = dynamic_cast<const F0Runner*>(&rhs);
111                return r && r->f == f;
112            }
113        };
114    /** Member function runner -- taking no argument (data is `this`), returning an int */
115    template <class T, class Ret>
116    struct MFRunner : public Runner {
117        Ret (T::*f)();
118        MFRunner(Ret (T::*mf)(), bool clean = false) : Runner(clean), f(mf) {}
119        virtual int run(void* data) {return (static_cast<T*>(data)->*f)() ;}
120        virtual bool operator==(const Runner &rhs) const {
121            const MFRunner *r = dynamic_cast<const MFRunner*>(&rhs);
122            return r && r->f == f;
123        }
124    };
125    /** Match the member function runner */
126    template <class T, class Ret>
127    static Runner* runner(Ret (T::*func)() ) {
128        return new MFRunner<T, Ret>(func);
129    }
130    /** Match no-arg runner for functions */
131    template <class Ret>
132    static Runner* runner(Ret (*func)() ) {
133        return new F0Runner<Ret (*)()>(func);
134    }
135    /** Match 1-arg runner for functions */
136    template <class Ret, class Arg>
137    static Runner* runner(Ret (*func)(Arg*) ) {
138        return new F1Runner<Ret (*)(Arg*), Arg>(func);
139    }
140    /** Match functors with no arguments */
141    template <class F>
142    static Runner* runner(const F& func) {
143        return new F0Runner<F>(func);
144    }
145    /** Match functors with 1 argument -- needs a different name! */
146    template <class Arg, class F>
147    static Runner* runner1(const F& func) {
148        return new F1Runner<F, Arg>(func);
149    }
150    //@}
151
152    /** Constructor -- creates the mutex and syncman */
153    ThreadMan();
154    /** Destructor -- destroys mutex and syncman */
155    ~ThreadMan();
156
157private:
158    /** Cleanup struct -- ThreadMan repeatedly calls done(); to ask if it is finished */
159    struct ThreadKiller {
160        SDL_Thread *thr;      ///< Handle on the thread
161        bool finished;        ///< Set to true by the thread, when entry point returns
162
163        /** Cleanup the thread, if we know we won't block, and return true */
164        bool done();
165        int wait();
166
167        /** Constructor just copies the thread handle */
168        ThreadKiller(SDL_Thread *t);
169        /** Compare == if we handle the same thread */
170        bool operator==(SDL_Thread *t);
171    };
172
173    typedef std::vector<ThreadKiller> THREADS; ///< type for managed threads
174
175    THREADS threads;      ///< Managed threads
176    SDL_mutex *thrmut;    ///< Mutex for accessing \a threads
177    SyncThreads *syncman; ///< Synchronised threads
178
179    /** Check for finished threads to Wait/Join */
180    void thread_cleanup();
181
182    ThreadMan& operator=(const ThreadMan& rhs); ///< No Assignment
183    ThreadMan(const ThreadMan& rhs);            ///< No copy construction
184public:
185    /**\internal Indicate that we can now call SDL_WaitThread(\a t) without blocking */
186    void thread_done(SDL_Thread *t);
187    /**\internal Add a killer for \a t */
188    void thread_add(SDL_Thread *t);
189};
190
191#endif
Note: See TracBrowser for help on using the browser.