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