1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21 */
22
23 namespace juce
24 {
25
TimeSliceThread(const String & name)26 TimeSliceThread::TimeSliceThread (const String& name) : Thread (name)
27 {
28 }
29
~TimeSliceThread()30 TimeSliceThread::~TimeSliceThread()
31 {
32 stopThread (2000);
33 }
34
35 //==============================================================================
addTimeSliceClient(TimeSliceClient * const client,int millisecondsBeforeStarting)36 void TimeSliceThread::addTimeSliceClient (TimeSliceClient* const client, int millisecondsBeforeStarting)
37 {
38 if (client != nullptr)
39 {
40 const ScopedLock sl (listLock);
41 client->nextCallTime = Time::getCurrentTime() + RelativeTime::milliseconds (millisecondsBeforeStarting);
42 clients.addIfNotAlreadyThere (client);
43 notify();
44 }
45 }
46
removeTimeSliceClient(TimeSliceClient * const client)47 void TimeSliceThread::removeTimeSliceClient (TimeSliceClient* const client)
48 {
49 const ScopedLock sl1 (listLock);
50
51 // if there's a chance we're in the middle of calling this client, we need to
52 // also lock the outer lock..
53 if (clientBeingCalled == client)
54 {
55 const ScopedUnlock ul (listLock); // unlock first to get the order right..
56
57 const ScopedLock sl2 (callbackLock);
58 const ScopedLock sl3 (listLock);
59
60 clients.removeFirstMatchingValue (client);
61 }
62 else
63 {
64 clients.removeFirstMatchingValue (client);
65 }
66 }
67
removeAllClients()68 void TimeSliceThread::removeAllClients()
69 {
70 for (;;)
71 {
72 if (auto* c = getClient (0))
73 removeTimeSliceClient (c);
74 else
75 break;
76 }
77 }
78
moveToFrontOfQueue(TimeSliceClient * client)79 void TimeSliceThread::moveToFrontOfQueue (TimeSliceClient* client)
80 {
81 const ScopedLock sl (listLock);
82
83 if (clients.contains (client))
84 {
85 client->nextCallTime = Time::getCurrentTime();
86 notify();
87 }
88 }
89
getNumClients() const90 int TimeSliceThread::getNumClients() const
91 {
92 return clients.size();
93 }
94
getClient(const int i) const95 TimeSliceClient* TimeSliceThread::getClient (const int i) const
96 {
97 const ScopedLock sl (listLock);
98 return clients[i];
99 }
100
101 //==============================================================================
getNextClient(int index) const102 TimeSliceClient* TimeSliceThread::getNextClient (int index) const
103 {
104 Time soonest;
105 TimeSliceClient* client = nullptr;
106
107 for (int i = clients.size(); --i >= 0;)
108 {
109 auto* c = clients.getUnchecked ((i + index) % clients.size());
110
111 if (client == nullptr || c->nextCallTime < soonest)
112 {
113 client = c;
114 soonest = c->nextCallTime;
115 }
116 }
117
118 return client;
119 }
120
run()121 void TimeSliceThread::run()
122 {
123 int index = 0;
124
125 while (! threadShouldExit())
126 {
127 int timeToWait = 500;
128
129 {
130 Time nextClientTime;
131 int numClients = 0;
132
133 {
134 const ScopedLock sl2 (listLock);
135
136 numClients = clients.size();
137 index = numClients > 0 ? ((index + 1) % numClients) : 0;
138
139 if (auto* firstClient = getNextClient (index))
140 nextClientTime = firstClient->nextCallTime;
141 }
142
143 if (numClients > 0)
144 {
145 auto now = Time::getCurrentTime();
146
147 if (nextClientTime > now)
148 {
149 timeToWait = (int) jmin ((int64) 500, (nextClientTime - now).inMilliseconds());
150 }
151 else
152 {
153 timeToWait = index == 0 ? 1 : 0;
154
155 const ScopedLock sl (callbackLock);
156
157 {
158 const ScopedLock sl2 (listLock);
159 clientBeingCalled = getNextClient (index);
160 }
161
162 if (clientBeingCalled != nullptr)
163 {
164 const int msUntilNextCall = clientBeingCalled->useTimeSlice();
165
166 const ScopedLock sl2 (listLock);
167
168 if (msUntilNextCall >= 0)
169 clientBeingCalled->nextCallTime = now + RelativeTime::milliseconds (msUntilNextCall);
170 else
171 clients.removeFirstMatchingValue (clientBeingCalled);
172
173 clientBeingCalled = nullptr;
174 }
175 }
176 }
177 }
178
179 if (timeToWait > 0)
180 wait (timeToWait);
181 }
182 }
183
184 } // namespace juce
185