1 /****************************************************************************
2 * GCache                                                                    *
3 * Author: Federico Ponchio                                                  *
4 *                                                                           *
5 * Copyright(C) 2011                                                         *
6 * Visual Computing Lab                                                      *
7 * ISTI - Italian National Research Council                                  *
8 *                                                                           *
9 * All rights reserved.                                                      *
10 *                                                                           *
11 * This program is free software; you can redistribute it and/or modify      *
12 * it under the terms of the GNU General Public License as published by      *
13 * the Free Software Foundation; either version 2 of the License, or         *
14 * (at your option) any later version.                                       *
15 *                                                                           *
16 * This program is distributed in the hope that it will be useful,           *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
20 * for more details.                                                         *
21 *                                                                           *
22 ****************************************************************************/
23 
24 
25 #ifndef CACHE_DOOR_H
26 #define CACHE_DOOR_H
27 
28 #include <wrap/system/multithreading/mt.h>
29 #include <wrap/system/multithreading/atomic_int.h>
30 
31 #ifdef NEXUS_USE_QT
32 #include <QWaitCondition>
33 #endif
34 
35 #define METHOD_2
36 
37 #ifdef METHOD_1
38 
39 class QDoor {
40  private:
41   mt::semaphore door;
42   mt::mutex room;     //lock when entering. unlock when exiting
43   QAtomicInt key; //keep tracks of door status
44 
45  public:
QDoor()46   QDoor():key(0) {}
open()47   void open() {
48     if(key.testAndSetOrdered(0, 1))
49       door.release(1);
50   }
51 
enter()52   void enter() {
53     door.acquire(1); //here I am sure that key is 1
54     //if here a open appends will have no effect.
55     key.testAndSetOrdered(1, 0);
56     room.lock();
57   }
leave()58   void leave() {
59     room.unlock();
60   }
lock()61   void lock() {
62     int r = key.fetchAndStoreOrdered(-1);
63     if(r == 1) //if the door was open
64       door.tryAcquire(1); //might file if whe are between enter acquire and key = 0.
65   }
unlock()66   void unlock() {
67     key = 0;
68   }
69 };
70 #endif
71 
72 #ifdef METHOD_2
73 
74 //a door needs to be open for the thread to continue,
75 //if it is open the thread enter and closes the door
76 //this mess is to avoid [if(!open.available()) open.release(1)]
77 
78 class QDoor {
79  private:
80   mt::semaphore _open;
81   mt::semaphore _close;
82 
83  public:
84   mt::mutex room;
QDoor()85   QDoor(): _open(0), _close(1) {} //this means closed
86 
open()87   void open() {
88     if(_close.tryAcquire(1)) //check it is not open
89       _open.release(1); //open
90   }
close()91   void close() {
92     if(_open.tryAcquire(1)) //check not already closed
93       _close.release(1);
94   }
95   void enter(bool close = false) {
96     _open.acquire(1);
97     if(close)
98       _close.release(1); //close door behind
99     else
100       _open.release(1);  //leave door opened
101    room.lock();
102   }
leave()103   void leave() { room.unlock(); }
104 
lock()105   void lock() {
106     //door might be open or closed, but we might happen just in the middle
107     //of someone opening, closing or entering it.
108     while(!_open.tryAcquire(1) && !_close.tryAcquire(1)) {}
109     //no resources left, door is locked
110   }
111   void unlock(bool open = false) {
112     if(open)
113       _open.release(1);
114     else
115       _close.release(1);
116   }
isWaiting()117   bool isWaiting() {
118     if(_open.tryAcquire(1)) {
119       _close.release(1);
120       return false;
121     }
122     return true;
123   }
124 };
125 
126 
127 #endif
128 
129 
130 #ifdef METHOD_3
131 /**
132   A wait condition class that works as a door.
133   Should check if the semaphore version is faster.
134 */
135 
136 class QDoor {
137  public:
138 
QDoor(void)139   QDoor(void) : doorOpen(false), waiting(false) {}
140 
141   ///opens the door. Threads trying to enter will be awakened
open(void)142   void open(void) {
143     m.lock();
144     doorOpen = true;
145     m.unlock();
146     c.wakeAll(); arglebargle
147   }
148 
149   ///attempt to enter the door. if the door is closed the thread will wait until the door is opened.
150   /// if close is true, the door will be closed after the thread is awakened, this allows to
151   ///   have only one thread entering the door each time open() is called
152   void enter(bool close = false) {
153     m.lock();
154     waiting = true;
155     while (!doorOpen)
156       c.wait(&(m));
157 
158     if(close)
159       doorOpen = false;
160     waiting = false;
161     m.unlock();
162   }
leave()163   void leave() {}
isWaiting()164   bool isWaiting() {
165     m.lock();
166     bool w = waiting;
167     m.unlock();
168     return w;
169   }
lock()170   void lock() { //prevend door opening and entering
171     m.lock();
172   }
173   void unlock(bool open = false) { //reverse effect of lock
174     doorOpen = open;
175     m.unlock();
176   }
177  private:
178   mt::mutex m;
179   QWaitCondition c;
180   bool doorOpen;
181   bool waiting;
182 };
183 
184 #endif
185 
186 
187 #endif //CACHE_DOOR_H
188