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
13using std::nothrow;
14
15
16EventQueue::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
41EventQueue::~EventQueue()
42{
43	if (delete_sem(fThreadControl) == B_OK)
44		wait_for_thread(fEventExecutor, &fEventExecutor);
45	while (Event *event = (Event*)fEvents.RemoveItem((int32)0)) {
46		if (event->AutoDelete())
47			delete event;
48	}
49}
50
51
52status_t
53EventQueue::InitCheck()
54{
55	return fStatus;
56}
57
58
59EventQueue*
60EventQueue::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
71void
72EventQueue::DeleteDefault()
73{
74	if (fDefaultQueue) {
75		delete fDefaultQueue;
76		fDefaultQueue = NULL;
77	}
78}
79
80
81EventQueue&
82EventQueue::Default()
83{
84	return *fDefaultQueue;
85}
86
87
88void
89EventQueue::AddEvent(Event* event)
90{
91	Lock();
92	_AddEvent(event);
93	_Reschedule();
94	Unlock();
95}
96
97
98bool
99EventQueue::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
110void
111EventQueue::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.
124void
125EventQueue::_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
142Event*
143EventQueue::_EventAt(int32 index) const
144{
145	return (Event*)fEvents.ItemAtFast(index);
146}
147
148
149int32
150EventQueue::_execute_events_(void* cookie)
151{
152	EventQueue *gc = (EventQueue*)cookie;
153	return gc->_ExecuteEvents();
154}
155
156
157int32
158EventQueue::_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((int32)0);
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.
199void
200EventQueue::_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
211EventQueue*	EventQueue::fDefaultQueue = NULL;
212
213