xref: /haiku/src/apps/mediaplayer/support/EventQueue.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
1 /*
2  * Copyright (c) 2000-2008, Ingo Weinhold <ingo_weinhold@gmx.de>,
3  * Copyright (c) 2000-2008, Stephan Aßmus <superstippi@gmx.de>,
4  * All Rights Reserved. Distributed under the terms of the MIT license.
5  */
6 #include <new>
7 #include <stdio.h>
8 
9 #include "Event.h"
10 
11 #include "EventQueue.h"
12 
13 using std::nothrow;
14 
15 
16 EventQueue::EventQueue()
17 	: fEvents(100),
18 	  fEventExecutor(-1),
19 	  fThreadControl(-1),
20 	  fNextEventTime(0),
21 	  fStatus(B_ERROR)
22 
23 {
24 	fThreadControl = create_sem(0, "event queue control");
25 	if (fThreadControl >= B_OK)
26 		fStatus = B_OK;
27 	else
28 		fStatus = fThreadControl;
29 	if (fStatus == B_OK) {
30 		fEventExecutor = spawn_thread(_execute_events_, "event queue runner",
31 									  B_NORMAL_PRIORITY, this);
32 		if (fEventExecutor >= B_OK) {
33 			fStatus = B_OK;
34 			resume_thread(fEventExecutor);
35 		} else
36 			fStatus = fEventExecutor;
37 	}
38 }
39 
40 
41 EventQueue::~EventQueue()
42 {
43 	if (delete_sem(fThreadControl) == B_OK)
44 		wait_for_thread(fEventExecutor, &fEventExecutor);
45 	while (Event *event = (Event*)fEvents.RemoveItem(0L)) {
46 		if (event->AutoDelete())
47 			delete event;
48 	}
49 }
50 
51 
52 status_t
53 EventQueue::InitCheck()
54 {
55 	return fStatus;
56 }
57 
58 
59 EventQueue*
60 EventQueue::CreateDefault()
61 {
62 	if (!fDefaultQueue) {
63 		fDefaultQueue = new(nothrow) EventQueue;
64 		if (fDefaultQueue && fDefaultQueue->InitCheck() != B_OK)
65 			DeleteDefault();
66 	}
67 	return fDefaultQueue;
68 }
69 
70 
71 void
72 EventQueue::DeleteDefault()
73 {
74 	if (fDefaultQueue) {
75 		delete fDefaultQueue;
76 		fDefaultQueue = NULL;
77 	}
78 }
79 
80 
81 EventQueue&
82 EventQueue::Default()
83 {
84 	return *fDefaultQueue;
85 }
86 
87 
88 void
89 EventQueue::AddEvent(Event* event)
90 {
91 	Lock();
92 	_AddEvent(event);
93 	_Reschedule();
94 	Unlock();
95 }
96 
97 
98 bool
99 EventQueue::RemoveEvent(Event* event)
100 {
101 	bool result = false;
102 	Lock();
103 	if ((result = fEvents.RemoveItem(event)))
104 		_Reschedule();
105 	Unlock();
106 	return result;
107 }
108 
109 
110 void
111 EventQueue::ChangeEvent(Event* event, bigtime_t newTime)
112 {
113 	Lock();
114 	if (fEvents.RemoveItem(event)) {
115 		event->SetTime(newTime);
116 		_AddEvent(event);
117 		_Reschedule();
118 	}
119 	Unlock();
120 }
121 
122 
123 // PRE: The object must be locked.
124 void
125 EventQueue::_AddEvent(Event* event)
126 {
127 	// find the insertion index
128 	int32 lower = 0;
129 	int32 upper = fEvents.CountItems();
130 	while (lower < upper) {
131 		int32 mid = (lower + upper) / 2;
132 		Event* midEvent = _EventAt(mid);
133 		if (event->Time() < midEvent->Time())
134 			upper = mid;
135 		else
136 			lower = mid + 1;
137 	}
138 	fEvents.AddItem(event, lower);
139 }
140 
141 
142 Event*
143 EventQueue::_EventAt(int32 index) const
144 {
145 	return (Event*)fEvents.ItemAtFast(index);
146 }
147 
148 
149 int32
150 EventQueue::_execute_events_(void* cookie)
151 {
152 	EventQueue *gc = (EventQueue*)cookie;
153 	return gc->_ExecuteEvents();
154 }
155 
156 
157 int32
158 EventQueue::_ExecuteEvents()
159 {
160 	bool running = true;
161 	while (running) {
162 		bigtime_t waitUntil = B_INFINITE_TIMEOUT;
163 		if (Lock()) {
164 			if (!fEvents.IsEmpty())
165 				waitUntil = _EventAt(0)->Time();
166 			fNextEventTime = waitUntil;
167 			Unlock();
168 		}
169 		status_t err = acquire_sem_etc(fThreadControl, 1, B_ABSOLUTE_TIMEOUT,
170 									   waitUntil);
171 		switch (err) {
172 			case B_TIMED_OUT:
173 				// execute events, that are supposed to go off
174 				if (Lock()) {
175 					while (!fEvents.IsEmpty()
176 						   && system_time() >= _EventAt(0)->Time()) {
177 						Event* event = (Event*)fEvents.RemoveItem(0L);
178 						bool deleteEvent = event->AutoDelete();
179 						event->Execute();
180 						if (deleteEvent)
181 							delete event;
182 					}
183 					Unlock();
184 				}
185 				break;
186 			case B_BAD_SEM_ID:
187 				running = false;
188 				break;
189 			case B_OK:
190 			default:
191 				break;
192 		}
193 	}
194 	return 0;
195 }
196 
197 
198 // PRE: The object must be locked.
199 void
200 EventQueue::_Reschedule()
201 {
202 	if (fStatus == B_OK) {
203 		if (!fEvents.IsEmpty() && _EventAt(0)->Time() < fNextEventTime)
204 			release_sem(fThreadControl);
205 	}
206 }
207 
208 
209 // static variables
210 
211 EventQueue*	EventQueue::fDefaultQueue = NULL;
212 
213