/[svn]/linuxsampler/trunk/src/engines/InstrumentManagerThread.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/engines/InstrumentManagerThread.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3766 - (show annotations) (download)
Mon Apr 6 12:41:49 2020 UTC (3 years, 11 months ago) by schoenebeck
File size: 8652 byte(s)
Fixed deadlocks (e.g. when restarting engines).

* Individual thread implementations (e.g. disk thread, etc.):
  Disable thread cancellation on critical sections, e.g. when holding
  mutex locks, to prevent deadlocks if thread is stopped and/or
  restarted.

* Added TestCancel() calls to thread implementations if missing.

* No need to wrap Thread::TestCancel() calls into
  CONFIG_PTHREAD_TESTCANCEL macro conditions (since TestCancel() is
  already a stub on systems which don't have pthread_testcancel()
  available).

* If compiled for debugging: give each thread a human readable name
  to simplify debugging of multi-threading issues.

* DiskThreadBase: TestCancel() and pthread_testcancel() calls are
  per-se redundant, so only call TestCancel().

* Added missing override keywords to silent compiler warnings.

* Bumped version (2.1.1.svn54).

1 /***************************************************************************
2 * *
3 * Copyright (C) 2005 - 2020 Christian Schoenebeck *
4 * *
5 * This library is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This library is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this library; if not, write to the Free Software *
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
18 * MA 02111-1307 USA *
19 ***************************************************************************/
20
21 #include "InstrumentManagerThread.h"
22
23 #include "../common/global_private.h"
24 #include "EngineChannelFactory.h"
25
26 namespace LinuxSampler {
27
28 InstrumentManagerThread::InstrumentManagerThread() : Thread(true, false, 0, -4) {
29 eventHandler.pThread = this;
30 }
31
32 InstrumentManagerThread::~InstrumentManagerThread() {
33 Thread::StopThread();
34 }
35
36 /**
37 * @brief Order loading of a new instrument.
38 *
39 * The request will go into a queue waiting to be processed by the
40 * class internal task thread. This method will immediately return and
41 * the instrument will be loaded in the background.
42 *
43 * @param Filename - file name of the instrument
44 * @param uiInstrumentIndex - index of the instrument within the file
45 * @param pEngineChannel - engine channel on which the instrument should be loaded
46 */
47 void InstrumentManagerThread::StartNewLoad(String Filename, uint uiInstrumentIndex, EngineChannel* pEngineChannel) {
48 dmsg(1,("Scheduling '%s' (Index=%d) to be loaded in background (if not loaded yet).\n",Filename.c_str(),uiInstrumentIndex));
49
50 // the listener only needs to be registered once in the
51 // Sampler, but as we don't know if Sampler has been
52 // recreated, we simply remove and add every time
53 pEngineChannel->GetSampler()->RemoveChannelCountListener(&eventHandler);
54 pEngineChannel->GetSampler()->AddChannelCountListener(&eventHandler);
55
56 command_t cmd;
57 cmd.type = command_t::DIRECT_LOAD;
58 cmd.pEngineChannel = pEngineChannel;
59 cmd.instrumentId.Index = uiInstrumentIndex;
60 cmd.instrumentId.FileName = Filename;
61
62 {
63 LockGuard lock(mutex);
64 queue.push_back(cmd);
65 }
66
67 StartThread(); // ensure thread is running
68 conditionJobsLeft.Set(true); // wake up thread
69 }
70
71 /**
72 * @brief Order changing the life-time strategy of an instrument.
73 *
74 * The request will go into a queue waiting to be processed by the
75 * class internal task thread. This method will immediately return and
76 * in case the instrument has to be loaded due to a mode change to
77 * PERSISTENT, it will be loaded in the background.
78 *
79 * @param pManager - InstrumentManager which manages the instrument
80 * @param ID - unique ID of the instrument
81 * @param Mode - life-time strategy to set for this instrument
82 */
83 void InstrumentManagerThread::StartSettingMode(InstrumentManager* pManager, const InstrumentManager::instrument_id_t& ID, InstrumentManager::mode_t Mode) {
84 command_t cmd;
85 cmd.type = command_t::INSTR_MODE;
86 cmd.pManager = pManager;
87 cmd.instrumentId = ID;
88 cmd.mode = Mode;
89
90 {
91 LockGuard lock(mutex);
92 queue.push_back(cmd);
93 }
94
95 StartThread(); // ensure thread is running
96 conditionJobsLeft.Set(true); // wake up thread
97 }
98
99 // Entry point for the task thread.
100 int InstrumentManagerThread::Main() {
101 #if DEBUG
102 Thread::setNameOfCaller("InstrumentMngr");
103 #endif
104
105 while (true) {
106
107 TestCancel();
108
109 // prevent thread from being cancelled
110 // (e.g. to prevent deadlocks while holding mutex lock(s))
111 pushCancelable(false);
112
113 while (true) {
114 command_t cmd;
115
116 // grab a new command from the queue
117 {
118 LockGuard lock(mutex);
119 if (queue.empty()) break;
120
121 cmd = queue.front();
122 queue.pop_front();
123
124 if (cmd.type == command_t::DIRECT_LOAD) {
125 EngineChannelFactory::SetDeleteEnabled(cmd.pEngineChannel, false);
126 }
127 }
128
129 try {
130 switch (cmd.type) {
131 case command_t::DIRECT_LOAD:
132 cmd.pEngineChannel->PrepareLoadInstrument(cmd.instrumentId.FileName.c_str(), cmd.instrumentId.Index);
133 cmd.pEngineChannel->LoadInstrument();
134 EngineChannelFactory::SetDeleteEnabled(cmd.pEngineChannel, true);
135 break;
136 case command_t::INSTR_MODE:
137 cmd.pManager->SetMode(cmd.instrumentId, cmd.mode);
138 break;
139 default:
140 std::cerr << "InstrumentManagerThread: unknown command - BUG!\n" << std::flush;
141 }
142 } catch (Exception e) {
143 e.PrintMessage();
144 if (cmd.type == command_t::DIRECT_LOAD) {
145 EngineChannelFactory::SetDeleteEnabled(cmd.pEngineChannel, true);
146 }
147 } catch (...) {
148 std::cerr << "InstrumentManagerThread: some exception occured, could not finish task\n" << std::flush;
149 if (cmd.type == command_t::DIRECT_LOAD) {
150 EngineChannelFactory::SetDeleteEnabled(cmd.pEngineChannel, true);
151 }
152 }
153 }
154
155 // now allow thread being cancelled again
156 // (since all mutexes are now unlocked)
157 popCancelable();
158
159 // nothing left to do, sleep until new jobs arrive
160 conditionJobsLeft.WaitIf(false);
161 // reset flag
162 conditionJobsLeft.Set(false);
163 // unlock condition object so it can be turned again by other thread
164 conditionJobsLeft.Unlock();
165 }
166 return 0;
167 }
168
169 void InstrumentManagerThread::EventHandler::ChannelToBeRemoved(SamplerChannel* pChannel) {
170 /*
171 Removing from the queue an eventual scheduled loading of an instrument
172 to a sampler channel which is going to be removed.
173 */
174 LockGuard lock(pThread->mutex);
175 std::list<command_t>::iterator it;
176 for (it = pThread->queue.begin(); it != pThread->queue.end();){
177 if ((*it).type != command_t::DIRECT_LOAD) { ++it; continue; }
178 if ((*it).pEngineChannel == pChannel->GetEngineChannel()) {
179 it = pThread->queue.erase(it);
180 // we don't break here because the same engine channel could
181 // occur more than once in the queue, so don't make optimizations
182 } else {
183 ++it;
184 }
185 }
186 }
187
188 #if defined(__APPLE__) && !defined(__x86_64__)
189 int InstrumentManagerThread::StopThread() {
190 // This is a fix for Mac OS X 32 bit, where SignalStopThread
191 // doesn't wake up a thread waiting for a condition variable.
192 SignalStopThread(); // send stop signal, but don't wait
193 conditionJobsLeft.Set(true); // wake thread
194 return Thread::StopThread(); // then wait for it to cancel
195 }
196 #endif
197
198 #ifdef WIN32
199 int InstrumentManagerThread::StopThread() {
200 int res = Thread::StopThread();
201 conditionJobsLeft.Reset();
202 return res;
203 }
204 #endif
205
206 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC