1 /**
2  * Copyright (c) 2006-2016 LOVE Development Team
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty.  In no event will the authors be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  *    claim that you wrote the original software. If you use this software
14  *    in a product, an acknowledgment in the product documentation would be
15  *    appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  *    misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  **/
20 
21 #include "Channel.h"
22 #include <map>
23 #include <string>
24 
25 namespace
26 {
27 union uslong
28 {
29 	unsigned long u;
30 	long i;
31 };
32 
33 // target <= current, but semi-wrapsafe, one wrap, anyway
past(unsigned int target,unsigned int current)34 inline bool past(unsigned int target, unsigned int current)
35 {
36 	if (target > current)
37 		return false;
38 	if (target == current)
39 		return true;
40 
41 	uslong t, c;
42 	t.u = target;
43 	c.u = current;
44 
45 	return !(t.i < 0 && c.i > 0);
46 }
47 }
48 
49 namespace love
50 {
51 namespace thread
52 {
53 static std::map<std::string, Channel *> namedChannels;
54 static Mutex *namedChannelMutex;
55 
getChannel(const std::string & name)56 Channel *Channel::getChannel(const std::string &name)
57 {
58 	if (!namedChannelMutex)
59 		namedChannelMutex = newMutex();
60 
61 	Lock lock(namedChannelMutex);
62 
63 	auto it = namedChannels.find(name);
64 
65 	if (it != namedChannels.end())
66 	{
67 		it->second->retain();
68 		return it->second;
69 	}
70 
71 	namedChannels[name] = new Channel(name);
72 	return namedChannels[name];
73 }
74 
Channel()75 Channel::Channel()
76 	: named(false)
77 	, sent(0)
78 	, received(0)
79 {
80 }
81 
Channel(const std::string & name)82 Channel::Channel(const std::string &name)
83 	: named(true)
84 	, name(name)
85 	, sent(0)
86 	, received(0)
87 {
88 }
89 
~Channel()90 Channel::~Channel()
91 {
92 	if (named)
93 	{
94 		Lock l(namedChannelMutex);
95 		namedChannels.erase(name);
96 	}
97 }
98 
push(const Variant & var)99 unsigned long Channel::push(const Variant &var)
100 {
101 	Lock l(mutex);
102 
103 	// Keep a reference to ourselves
104 	// if we're non-empty and named.
105 	if (named && queue.empty())
106 		retain();
107 
108 	queue.push(var);
109 	cond->broadcast();
110 
111 	return ++sent;
112 }
113 
supply(const Variant & var)114 void Channel::supply(const Variant &var)
115 {
116 	Lock l(mutex);
117 	unsigned long id = push(var);
118 
119 	while (!past(id, received))
120 		cond->wait(mutex);
121 }
122 
pop(Variant * var)123 bool Channel::pop(Variant *var)
124 {
125 	Lock l(mutex);
126 
127 	if (queue.empty())
128 		return false;
129 
130 	*var = queue.front();
131 	queue.pop();
132 
133 	received++;
134 	cond->broadcast();
135 
136 	// Release our reference to ourselves
137 	// if we're empty and named.
138 	if (named && queue.empty())
139 		release();
140 
141 	return true;
142 }
143 
demand(Variant * var)144 void Channel::demand(Variant *var)
145 {
146 	Lock l(mutex);
147 
148 	while (!pop(var))
149 		cond->wait(mutex);
150 }
151 
peek(Variant * var)152 bool Channel::peek(Variant *var)
153 {
154 	Lock l(mutex);
155 
156 	if (queue.empty())
157 		return false;
158 
159 	*var = queue.front();
160 	return true;
161 }
162 
getCount()163 int Channel::getCount()
164 {
165 	Lock l(mutex);
166 	return (int) queue.size();
167 }
168 
clear()169 void Channel::clear()
170 {
171 	Lock l(mutex);
172 
173 	// We're already empty.
174 	if (queue.empty())
175 		return;
176 
177 	while (!queue.empty())
178 		queue.pop();
179 
180 	// Finish all the supply waits
181 	received = sent;
182 	cond->broadcast();
183 
184 	// Once again, release our own
185 	// reference if we're named.
186 	if (named)
187 		release();
188 }
189 
lockMutex()190 void Channel::lockMutex()
191 {
192 	mutex->lock();
193 }
194 
unlockMutex()195 void Channel::unlockMutex()
196 {
197 	mutex->unlock();
198 }
199 
200 } // thread
201 } // love
202