aboutsummaryrefslogtreecommitdiff
path: root/src/stable/counterevent.h
blob: 25845b04489c9cf58038dad75386c10df973f079 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 * Copyright (C) 2007-2023 Xagasoft, All rights reserved.
 *
 * This file is part of the libbu++ library and is released under the
 * terms of the license contained in the file LICENSE.
 */

#ifndef BU_COUNTER_EVENT_H
#define BU_COUNTER_EVENT_H

#include <pthread.h>

#include "bu/mutex.h"
#include "bu/condition.h"

namespace Bu
{
    /**
     * Represents a true/false state that controls thread synchronization. This
     * is primarilly intended to control the synchronization state of
     * multithreaded services. For example, telling all threads when to exit.
     *
     * An Event is either set or unset. If the Event is unset then it can be
     * waited on for something to happen. As soon as the Event is set all
     * waiting threads are released and new requests to wait are ignored until
     * the Event is cleared again.
     *
     * Threads can also be woken up without setting the Event, which may be
     * handy in certain circumstances.
     *@ingroup Threading
     */
    class CounterEvent
    {
    public:
        CounterEvent() :
            iCount( 0 )
        {
        }
        
        ~CounterEvent()
        {
        }

        /**
         * Wait indefinitely for the Event to trigger. If the event is already
         * set, then return immediately.  It's important to note that this may
         * return at any time, not only when the Event is set, so examining the
         * return value is important.
         *@returns the set status of the Event.
         */
        int wait()
        {
            cBlock.lock();
            if( iCount == 0 )
            {
                cBlock.unlock();
                return iCount;
            }
            cBlock.wait();
            int iRet = iCount;
            cBlock.unlock();
            return iRet;
        }

        /**
         * Wait for up to nSec seconds and nUSec nanoseconds for the event to
         * trigger. If the Event is already set then return immediately.
         *@returns the set status of the Event.
         */
        int wait( int nSec, int nUSec )
        {
            cBlock.lock();
            if( iCount == 0 )
            {
                cBlock.unlock();
                return iCount;
            }
            cBlock.wait( nSec, nUSec );
            bool iRet = iCount;
            cBlock.unlock();
            return iRet;
        }

        /**
         * Allow one of the waiting threads to unlock without updating the set
         * state of the Event.
         */
        void unblockOne()
        {
            cBlock.lock();
            cBlock.signal();
            cBlock.unlock();
        }

        /**
         * Allow all waiting threads to unlock and proceed without updating the
         * set state of the Event.
         */
        void unblockAll()
        {
            cBlock.lock();
            cBlock.broadcast();
            cBlock.unlock();
        }

        /**
         * Find out if the Event is in the set state or not.
         *@returns True if set, false otherwise.
         */
        bool isZero()
        {
            cBlock.lock();
            bool bRet = (iCount == 0);
            cBlock.unlock();
            return bRet;
        }

        /**
         * Sets the Event's state to true and triggers all waiting threads.
         */
        void decrement()
        {
            cBlock.lock();
            iCount--; if( iCount < 0 ) iCount = 0;
            if( iCount == 0 )
                cBlock.broadcast();
            cBlock.unlock();
        }

        void increment()
        {
            cBlock.lock();
            iCount++;
            cBlock.unlock();
        }
        
        /**
         * Sets the Event's state to false. This does NOT trigger any waiting
         * threads.
         */
        void clear()
        {
            cBlock.lock();
            iCount = 0;
            cBlock.unlock();
        }

    private:
        Condition cBlock;   /**< The condition for blocking dequeues. */
        int iCount;
    };
}

#endif