root/threadman/trunk/semwaiter.h

Revision 615, 6.2 kB (checked in by tapted, 2 years ago)

Documentation

  • 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 SEMWAITER_DOT_AITCH
3#define SEMWAITER_DOT_AITCH
4
5/**\file semwaiter.h
6 * Declaration of SemWaiter
7 * \author Trent Apted <tapted@it.usyd.edu.au>
8 * $Rev$
9 * $Date$
10 */
11
12#include <SDL_thread.h>
13
14/** A simple C++ Sempahore wrapper */
15class SemWaiter {
16    /** Assignment is disabled */
17    SemWaiter& operator=(const SemWaiter&);
18protected:
19    SDL_sem *sem;      ///< The SDL Semaphore we are wrapping
20    volatile bool run; ///< Whether we keep running
21    Uint32 initial_resources; ///< How many resources are available for copies
22public:
23    /** Constructor creates the semaphore and inits data members */
24    SemWaiter (const SemWaiter& rhs)
25        :
26    sem(SDL_CreateSemaphore(rhs.initial_resources)), run(true), initial_resources(rhs.initial_resources) {}
27
28    /** Create a Semaphore, initially set to have \a "resources" */
29    explicit SemWaiter(Uint32 resources = 0)
30        :
31    sem(SDL_CreateSemaphore(resources)), run(true), initial_resources(resources) {}
32
33    /** Destroy the Semaphore */
34    ~SemWaiter() {SDL_DestroySemaphore(sem); sem = 0; run = false;}
35
36    /** Post to the Semaphore -- increment it -- notify next waiter that a resource is available */
37    void post() {SDL_SemPost(sem);}
38
39    /** Stop the SemWaiter -- don't allow further wait()s and post to (at most) 1 waiter waiting */
40    void stop() {run = false; post();}
41
42    /** Don't allow further wait()s to block, but don't post() */
43    void cancel() {run = false;}
44
45    /** Wrest control from another SemWaiter.
46     * Normally called from an object's ~Destructor() like
47     *\code
48     * destroy.wrest(thingo_ready, &thingo_processor);
49     *\endcode
50     * Where the last act of the thingo_processor thread will be to call
51     * stop().
52     * \see DestroyWaiter
53     */
54    int wrest(SemWaiter &alt, SDL_Thread **thr, Uint32 timeout = 0) {
55        alt.stop();
56        return threadwait(thr, timeout);
57    }
58    /** Wait for a thread to terminate, if non-null */
59    int threadwait(SDL_Thread **thr, Uint32 timeout = 0) {
60        int r = -1;
61        /* if \a thr is NULL, we assume the thread was never started,
62         * and so there will be no response to post to something waiting
63         * in that thread, hence no way for the next wait() to return
64         */
65        if (thr && *thr) {
66            if (timeout) {
67                if (!timedwait(true, timeout)) {
68                    SDL_KillThread(*thr);
69                    return -1;
70                }
71            } else {
72                wait();
73            }
74            SDL_WaitThread(*thr, &r);
75            *thr = 0;
76        }
77
78        return r;
79    }
80    /**
81     * Wait for this semaphore -- decrease available resources.
82     *
83     * \param spin If true we keep trying SemWait until it returns success (when debugging
84     *        for example, it is common for the system call to get interrupted here)
85     */
86    bool wait(bool spin = true) {
87        bool b = false;
88        while (run && (b = (SDL_SemWait(sem) == -1)) && spin)
89            ; //spin ==> !b || !run
90        return run && !b;
91    }
92    bool timedwait(bool spin = true, Uint32 timeout = 0) {
93        bool b = false;
94        while (run && (b = (SDL_SemWaitTimeout(sem, timeout) == -1)) && spin)
95            ; //spin ==> !b || !run
96        if (b == SDL_MUTEX_TIMEDOUT) {
97            /* timed out : b != 0 -> return false*/
98        }
99        return run && !b;
100    }
101    /** Overloaded Wait calls threadwait() */
102    int wait(SDL_Thread **thr, Uint32 timeout = 0) {return threadwait(thr, timeout);}
103
104    /** HAS SIDEFFECTS: returns wait() if we are running or false */
105    operator bool() {return run ? wait() : false;}
106
107    /** Return true if we are running */
108    bool running() const {return run;}
109
110    //virtual ~SemWaiter() {}
111};
112
113/** A SemWaiter for handling object destruction */
114class DestroyWaiter : private SemWaiter {
115    enum {DESTROYED_SEMVALUE = 2};
116public:
117    /** Creates a SemWaiter with a single resource */
118    DestroyWaiter() : SemWaiter(DESTROYED_SEMVALUE) {
119        while (SDL_SemValue(sem) != 1)
120            SDL_SemWait(sem);
121    }
122
123    /**
124     * Indicate that we want to destroy X
125     *
126     * \param alternate A SemWaiter that \a thr might be wait()ing on
127     * \param thr An optional thread; wait for this to complete after
128     *        stopping \a alternate
129     * \param timeout Timeout in milliseconds if we need to wrest control from the thread
130     */
131    int wants_to_destroy(SemWaiter &alternate, SDL_Thread **thr, Uint32 timeout = 0) {
132        if (!running())
133            return true;
134        return wrest(alternate, thr, timeout);
135    }
136
137    /**
138     * Indicate that we want to destroy X
139     *
140     * \param thr An optional thread that we will wait for completion
141     * \param timeout timeout for waits, in milliseconds
142     */
143    int wants_to_destroy(SDL_Thread **thr = 0, Uint32 timeout = 0) {
144        int r = -1;
145
146        if (timeout) {
147            if (!timedwait(true, timeout) && thr && *thr) {
148                /* probably timed out */
149                SDL_KillThread(*thr);
150                *thr = 0;
151            }
152            cancel();
153        } else {
154            cancel();
155            while (SDL_SemWait(sem) != 0)
156                ;
157            SDL_SemPost(sem); // never let it stay at 0
158        }
159        if (thr && *thr) {
160            SDL_WaitThread(*thr, &r);
161            *thr = 0;
162        }
163        /* there is a race condition here, but we don't really care */
164        while (SDL_SemValue(sem) < DESTROYED_SEMVALUE)
165            SDL_SemPost(sem);
166        return r;
167    }
168
169    /**
170     * Indicate to the thread that called wants_to_destroy() that X is
171     * now ready to destroy
172     */
173    void ready_to_destroy() {
174        stop();
175    }
176
177    /**
178     * Return true if this sem has been destroyed
179     */
180    bool hasDestroyed() {
181        return !run || SDL_SemValue(sem) >= DESTROYED_SEMVALUE;
182    }
183
184    /** Return the value of the semaphore */
185    Uint32 value() {
186        return SDL_SemValue(sem);
187    }
188
189    /**
190     * Cause wants_to_destroy to block -- the alternate / thr has started
191     */
192    bool alternate_started() {
193        return wait();
194    }
195
196    /** Return true if we are running */
197    bool running() const {return SemWaiter::running();}
198    void cancel() {SemWaiter::cancel();}
199};
200
201#endif
Note: See TracBrowser for help on using the browser.