/[svn]/linuxsampler/trunk/src/common/Condition.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/common/Condition.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3290 - (hide annotations) (download)
Fri Jun 23 12:24:58 2017 UTC (6 years, 9 months ago) by schoenebeck
File size: 18526 byte(s)
* Revised fundamental C++ classes "Thread", "Mutex" and
  "Condition" which fixes potential undefined behavior
  (note: this addresses mainly the POSIX implementation,
   Win32 is untested yet and would also need an update).
* Bumped version (2.0.0.svn64).

1 schoenebeck 53 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5 schoenebeck 56 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 schoenebeck 3290 * Copyright (C) 2005 - 2017 Christian Schoenebeck *
7 schoenebeck 53 * *
8     * This program is free software; you can redistribute it and/or modify *
9     * it under the terms of the GNU General Public License as published by *
10     * the Free Software Foundation; either version 2 of the License, or *
11     * (at your option) any later version. *
12     * *
13     * This program is distributed in the hope that it will be useful, *
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16     * GNU General Public License for more details. *
17     * *
18     * You should have received a copy of the GNU General Public License *
19     * along with this program; if not, write to the Free Software *
20     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21     * MA 02111-1307 USA *
22     ***************************************************************************/
23    
24 persson 417 #include <sys/time.h>
25    
26 schoenebeck 53 #include "Condition.h"
27    
28 schoenebeck 1424 #include "global_private.h"
29 schoenebeck 63
30 nagata 1649 #if CONFIG_PTHREAD_TESTCANCEL
31     #include "Thread.h"
32     #endif
33    
34 schoenebeck 1560 namespace LinuxSampler {
35    
36     // *************** Internal data types (for Windows only) ***************
37     // *
38    
39 senoner 1481 #if defined(WIN32)
40 schoenebeck 1560
41     typedef HANDLE win32thread_mutex_t;
42    
43 senoner 1481 //NOTE: since pthread_condattr_t is not needed in the Condition class so just set it to int
44     typedef int win32thread_condattr_t;
45    
46     typedef struct {
47 schoenebeck 1560 unsigned long tv_sec;
48     unsigned long tv_nsec;
49     } win32_timespec;
50 senoner 1481
51    
52 schoenebeck 1560
53     // *************** ConditionInternal (for Windows only) ***************
54     // *
55    
56     class ConditionInternal {
57     public:
58    
59     static
60     int win32thread_cond_init (Condition::win32thread_cond_t *cv, const win32thread_condattr_t *)
61 senoner 1481 {
62     dmsg(7,("win32thread_cond_init:\n"));
63     cv->waiters_count_ = 0;
64     cv->was_broadcast_ = 0;
65     cv->sema_ = CreateSemaphore (NULL, // no security
66     0, // initially 0
67     0x7fffffff, // max count
68 schoenebeck 1560 NULL); // unnamed
69     dmsg(7,("win32thread_cond_init: after CreateSemaphore retval=%d\n",cv->sema_));
70 senoner 1481 InitializeCriticalSection (&cv->waiters_count_lock_);
71     cv->waiters_done_ = CreateEvent (NULL, // no security
72     FALSE, // auto-reset
73     FALSE, // non-signaled initially
74     NULL); // unnamed
75 schoenebeck 1560 dmsg(7,("win32thread_cond_init: after CreateEvent retval=%d\n",cv->waiters_done_));
76    
77 senoner 1481 return 0;
78     }
79    
80 schoenebeck 1560 static
81     int win32thread_cond_destroy(Condition::win32thread_cond_t *cv) {
82     dmsg(7,("win32thread_cond_destroy ptr=%d\n",cv));
83 senoner 1481 CloseHandle(cv->waiters_done_);
84     DeleteCriticalSection(&cv->waiters_count_lock_);
85     CloseHandle(cv->sema_);
86     return 0;
87     }
88    
89 schoenebeck 1560 static
90     int win32thread_cond_timedwait (Condition::win32thread_cond_t *cv, win32thread_mutex_t *external_mutex, win32_timespec *timeout)
91 senoner 1481 {
92 schoenebeck 1560 dmsg(7,("win32thread_cond_timedwait: external mutex=%d BEGIN!\n",external_mutex));
93 senoner 1481 // Avoid race conditions.
94     dmsg(7,("win32thread_cond_timedwait: before EnterCriticalSection (&cv->waiters_count_lock_) cv->waiters_count_=%d\n",cv->waiters_count_));
95     EnterCriticalSection (&cv->waiters_count_lock_);
96     cv->waiters_count_++;
97     LeaveCriticalSection (&cv->waiters_count_lock_);
98     dmsg(7,("win32thread_cond_timedwait: after LeaveCriticalSection (&cv->waiters_count_lock_) cv->waiters_count_=%d\n",cv->waiters_count_));
99    
100 schoenebeck 1560
101 senoner 1481 // This call atomically releases the mutex and waits on the
102     // semaphore until <pthread_cond_signal> or <pthread_cond_broadcast>
103     // are called by another thread.
104 schoenebeck 1560
105 senoner 1481 DWORD dwMilliseconds;
106     if(timeout->tv_sec || timeout->tv_nsec) {
107     dwMilliseconds = timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000;
108     }
109     else {
110     dmsg(7,("win32thread_cond_timedwait: dwMilliseconds = INFINITE\n"));
111     dwMilliseconds = INFINITE;
112     }
113     dmsg(7,("win32thread_cond_timedwait: before SignalObjectAndWait(*external_mutex, cv->sema_, dwMilliseconds, FALSE), dwMilliseconds=%d\n",dwMilliseconds));
114     DWORD res;
115     res = SignalObjectAndWait (*external_mutex, cv->sema_, dwMilliseconds, FALSE);
116     dmsg(7,("win32thread_cond_timedwait: after SignalObjectAndWait, res=%d\n",res));
117 schoenebeck 1560 if(res == WAIT_TIMEOUT) return -1;
118 senoner 1481
119     // Reacquire lock to avoid race conditions.
120 schoenebeck 1560 dmsg(7,("win32thread_cond_timedwait: before EnterCriticalSection (2) (&cv->waiters_count_lock_) cv->waiters_count=%d\n",cv->waiters_count_));
121 senoner 1481 EnterCriticalSection (&cv->waiters_count_lock_);
122 schoenebeck 1560 dmsg(7,("win32thread_cond_timedwait: after EnterCriticalSection (2) (&cv->waiters_count_lock_) cv->waiters_count=%d\n",cv->waiters_count_));
123 senoner 1481
124     // We're no longer waiting...
125     cv->waiters_count_--;
126    
127     // Check to see if we're the last waiter after <pthread_cond_broadcast>.
128     int last_waiter = cv->was_broadcast_ && cv->waiters_count_ == 0;
129    
130     LeaveCriticalSection (&cv->waiters_count_lock_);
131 schoenebeck 1560 dmsg(7,("win32thread_cond_timedwait: after LeaveCriticalSection (2) (&cv->waiters_count_lock_) last_waiter=%d\n",last_waiter));
132 senoner 1481
133     // If we're the last waiter thread during this particular broadcast
134     // then let all the other threads proceed.
135     if (last_waiter) {
136     // This call atomically signals the <waiters_done_> event and waits until
137 schoenebeck 1560 // it can acquire the <external_mutex>. This is required to ensure fairness.
138 senoner 1481 dmsg(7,("win32thread_cond_timedwait: before SignalObjectAndWait (cv->waiters_done_, *external_mutex, dwMilliseconds, FALSE) \n"));
139     res = SignalObjectAndWait (cv->waiters_done_, *external_mutex, dwMilliseconds, FALSE);
140     dmsg(7,("win32thread_cond_timedwait: after SignalObjectAndWait (cv->waiters_done_, *external_mutex, dwMilliseconds, FALSE) res=%d\n",res));
141     if(res == WAIT_TIMEOUT) {
142     dmsg(7,("win32thread_cond_timedwait: after SignalObjectAndWait: WAIT_TIMEOUT! returning -1\n"));
143     return -1;
144     }
145     }
146     else {
147     // Always regain the external mutex since that's the guarantee we
148 schoenebeck 1560 // give to our callers.
149 senoner 1481 dmsg(7,("win32thread_cond_timedwait: before WaitForSingleObject (*external_mutex, dwMilliseconds)\n"));
150     res = WaitForSingleObject (*external_mutex, dwMilliseconds);
151     dmsg(7,("win32thread_cond_timedwait: after WaitForSingleObject (*external_mutex, dwMilliseconds) res=%d\n",res));
152     if(res == WAIT_TIMEOUT) {
153     dmsg(7,("win32thread_cond_timedwait: after WaitForSingleObject: WAIT_TIMEOUT ! returning -1\n"));
154     return -1;
155     }
156     }
157     dmsg(7,("win32thread_cond_timedwait: all OK. returning 0\n"));
158     return 0;
159     }
160    
161 schoenebeck 1560 static
162     int win32thread_cond_broadcast (Condition::win32thread_cond_t *cv)
163 senoner 1481 {
164     DWORD res;
165     dmsg(7,("win32thread_cond_broadcast: cv=%d\n",cv));
166     dmsg(7,("win32thread_cond_broadcast: before EnterCriticalSection (&cv->waiters_count_lock_)\n"));
167     // This is needed to ensure that <waiters_count_> and <was_broadcast_> are
168     // consistent relative to each other.
169     EnterCriticalSection (&cv->waiters_count_lock_);
170     int have_waiters = 0;
171    
172     dmsg(7,("win32thread_cond_broadcast: cv->waiters_count_=%d\n",cv->waiters_count_));
173     if (cv->waiters_count_ > 0) {
174     dmsg(7,("win32thread_cond_broadcast: cv->waiters_count > 0 !\n"));
175     // We are broadcasting, even if there is just one waiter...
176     // Record that we are broadcasting, which helps optimize
177     // <pthread_cond_wait> for the non-broadcast case.
178     cv->was_broadcast_ = 1;
179     have_waiters = 1;
180     }
181     dmsg(7,("win32thread_cond_broadcast: cv->was_broadcast_=%d have_waiters=%d\n",cv->was_broadcast_,have_waiters));
182    
183     if (have_waiters) {
184     // Wake up all the waiters atomically.
185     dmsg(7,("win32thread_cond_broadcast: have_waiters ! before ReleaseSemaphore (cv->sema_, cv->waiters_count_, 0);\n"));
186     ReleaseSemaphore (cv->sema_, cv->waiters_count_, 0);
187    
188     LeaveCriticalSection (&cv->waiters_count_lock_);
189     dmsg(7,("win32thread_cond_broadcast: have_waiters ! after LeaveCriticalSection (&cv->waiters_count_lock_)\n"));
190    
191     // Wait for all the awakened threads to acquire the counting
192 schoenebeck 1560 // semaphore.
193 senoner 1481 dmsg(7,("win32thread_cond_broadcast: before WaitForSingleObject (cv->waiters_done_, INFINITE)\n"));
194     res = WaitForSingleObject (cv->waiters_done_, INFINITE);
195     dmsg(7,("win32thread_cond_broadcast: after WaitForSingleObject (cv->waiters_done_, INFINITE) res=%d\n",res));
196 schoenebeck 1560 // This assignment is okay, even without the <waiters_count_lock_> held
197 senoner 1481 // because no other waiter threads can wake up to access it.
198     cv->was_broadcast_ = 0;
199     }
200     else {
201     LeaveCriticalSection (&cv->waiters_count_lock_);
202     dmsg(7,("win32thread_cond_broadcast: after LeaveCriticalSection (&cv->waiters_count_lock_)\n"));
203     }
204 schoenebeck 1560 dmsg(7,("win32thread_cond_broadcast: all OK, returning 0.\n"));
205 senoner 1481 return 0;
206     }
207    
208 schoenebeck 1560 /* TODO: the following two functions are currently not used yet
209     static
210     int win32thread_cond_signal (Condition::win32thread_cond_t *cv)
211     {
212 senoner 1481
213 schoenebeck 1560 dmsg(7,("win32thread_cond_signal cv=%d\n",cv));
214     dmsg(7,("win32thread_cond_signal before EnterCriticalSection (&cv->waiters_count_lock_)\n"));
215     EnterCriticalSection (&cv->waiters_count_lock_);
216     int have_waiters = cv->waiters_count_ > 0;
217     LeaveCriticalSection (&cv->waiters_count_lock_);
218     dmsg(7,("win32thread_cond_signal after LeaveCriticalSection (&cv->waiters_count_lock_)\n"));
219 senoner 1481
220 schoenebeck 1560 dmsg(7,("win32thread_cond_signal have_waiters=%d\n",have_waiters));
221     // If there aren't any waiters, then this is a no-op.
222     if (have_waiters) {
223     dmsg(7,("win32thread_cond have_waiters is TRUE, before ReleaseSemaphore (cv->sema_, 1, 0)\n"));
224     ReleaseSemaphore (cv->sema_, 1, 0);
225     }
226     dmsg(7,("win32thread_cond_signal: all OK. returning 0\n"));
227     return 0;
228     }
229 senoner 1481
230 schoenebeck 1560 static
231     int win32thread_cond_wait (Condition::win32thread_cond_t *cv, win32thread_mutex_t *external_mutex) {
232     dmsg(7,("win32thread_cond_wait: (calls win32thread_cond_timedwait) cv=%d external_mutex=%d\n",cv, external_mutex));
233     win32_timespec timeout;
234     timeout.tv_sec = 0;
235     timeout.tv_nsec = 0;
236     return win32thread_cond_timedwait (cv, external_mutex, &timeout);
237     }
238     */
239 senoner 1481
240 schoenebeck 1560 }; // class ConditionInternal
241 schoenebeck 880
242 schoenebeck 1560 #endif // WIN32
243    
244    
245    
246     // *************** Condition ***************
247     // *
248    
249 schoenebeck 3290 Condition::Condition(bool bInitialCondition, Mutex::type_t mutexType)
250     : Mutex(mutexType)
251     {
252 senoner 1481 dmsg(7,("Condition:: constructor, bInitialCondition=%d\n", bInitialCondition));
253     #if defined(WIN32)
254 schoenebeck 1560 ConditionInternal::win32thread_cond_init(&__win32_true_condition, NULL);
255     ConditionInternal::win32thread_cond_init(&__win32_false_condition, NULL);
256 senoner 1481 #else
257 schoenebeck 53 pthread_cond_init(&__posix_true_condition, NULL);
258     pthread_cond_init(&__posix_false_condition, NULL);
259 schoenebeck 1560 #endif
260 schoenebeck 53 bCondition = bInitialCondition;
261     }
262    
263     Condition::~Condition() {
264 senoner 1481 #if defined(WIN32)
265 schoenebeck 1560 ConditionInternal::win32thread_cond_destroy(&__win32_true_condition);
266     ConditionInternal::win32thread_cond_destroy(&__win32_false_condition);
267 senoner 1481 #else
268 schoenebeck 53 pthread_cond_destroy(&__posix_true_condition);
269     pthread_cond_destroy(&__posix_false_condition);
270 senoner 1481 #endif
271 schoenebeck 53 }
272    
273 persson 2324 #ifndef WIN32
274     namespace {
275     // If the thread is cancelled while waiting for the condition
276     // variable, the mutex will be locked. It needs to be unlocked so
277     // the Condition can be reused if the thread is restarted.
278     void condition_cleanup(void* c) {
279     static_cast<Condition*>(c)->Unlock();
280     }
281     }
282     #endif
283    
284 schoenebeck 3290 int Condition::WaitIfInternal(bool bLock, bool bCondition, long TimeoutSeconds, long TimeoutNanoSeconds) {
285     dmsg(7,("Condition::WaitIfInternal: bCondition=%d TimeoutSeconds=%ld TimeoutNanoSeconds=%ld\n",bCondition, TimeoutSeconds, TimeoutNanoSeconds));
286     if (bLock) {
287     dmsg(7,("Condition::WaitIfInternal() -> LOCK()\n"));
288     Lock();
289     dmsg(7,("Condition::WaitIfInternal() -> LOCK() passed\n"));
290     }
291 schoenebeck 53 int res = 0;
292 persson 2324 #ifndef WIN32
293     pthread_cleanup_push(condition_cleanup, this);
294     #endif
295 schoenebeck 53 if (this->bCondition == bCondition) {
296 schoenebeck 63 if (bCondition) { // wait until condition turned 'false'
297 senoner 1481 #if defined(WIN32)
298     win32_timespec timeout;
299     timeout.tv_sec = TimeoutSeconds;
300     timeout.tv_nsec = TimeoutNanoSeconds;
301 schoenebeck 3290 dmsg(7,("Condition::WaitIfInternal() -> waiting for 'false' condition with timeout\n"));
302 schoenebeck 1560 res = ConditionInternal::win32thread_cond_timedwait(&__win32_false_condition, &hMutex, &timeout);
303 schoenebeck 3290 dmsg(7,("Condition::WaitIfInternal() -> awakened from 'false' condition waiting\n"));
304 senoner 1481 #else
305 schoenebeck 63 if (TimeoutSeconds || TimeoutNanoSeconds) { // wait with timeout
306 persson 417 struct timeval now;
307     gettimeofday(&now, 0);
308 schoenebeck 53 timespec timeout;
309 persson 417 timeout.tv_sec = now.tv_sec + TimeoutSeconds;
310     timeout.tv_nsec = now.tv_usec * 1000 + TimeoutNanoSeconds;
311 schoenebeck 3290 dmsg(7,("Condition::WaitIfInternal() -> waiting for 'false' condition with timeout\n"));
312 schoenebeck 53 res = pthread_cond_timedwait(&__posix_false_condition, &__posix_mutex, &timeout);
313 schoenebeck 3290 dmsg(7,("Condition::WaitIfInternal() -> awakened from 'false' condition waiting\n"));
314 schoenebeck 53 }
315 schoenebeck 63 else { // wait without timeout
316 schoenebeck 3290 dmsg(7,("Condition::WaitIfInternal() -> waiting for 'false' condition\n"));
317 schoenebeck 63 pthread_cond_wait(&__posix_false_condition, &__posix_mutex);
318 schoenebeck 3290 dmsg(7,("Condition::WaitIfInternal() -> awakened from 'false' condition waiting\n"));
319 schoenebeck 63 }
320 senoner 1481 #endif
321 schoenebeck 53 }
322 schoenebeck 63 else { // wait until condition turned 'true'
323 senoner 1481 #if defined(WIN32)
324     win32_timespec timeout;
325     timeout.tv_sec = TimeoutSeconds;
326     timeout.tv_nsec = TimeoutNanoSeconds;
327 schoenebeck 3290 dmsg(7,("Condition::WaitIfInternal() -> waiting for 'true' condition with timeout\n"));
328 schoenebeck 1560 res = ConditionInternal::win32thread_cond_timedwait(&__win32_true_condition, &hMutex, &timeout);
329 schoenebeck 3290 dmsg(7,("Condition::WaitIfInternal() -> awakened from 'true' condition waiting\n"));
330 senoner 1481 #else
331 schoenebeck 63 if (TimeoutSeconds || TimeoutNanoSeconds) { // wait with timeout
332 persson 417 struct timeval now;
333     gettimeofday(&now, 0);
334 schoenebeck 53 timespec timeout;
335 persson 417 timeout.tv_sec = now.tv_sec + TimeoutSeconds;
336     timeout.tv_nsec = now.tv_usec * 1000 + TimeoutNanoSeconds;
337 schoenebeck 3290 dmsg(7,("Condition::WaitIfInternal() -> waiting for 'true' condition with timeout\n"));
338 schoenebeck 53 res = pthread_cond_timedwait(&__posix_true_condition, &__posix_mutex, &timeout);
339 schoenebeck 3290 dmsg(7,("Condition::WaitIfInternal() -> awakened from 'true' condition waiting\n"));
340 schoenebeck 53 }
341 schoenebeck 63 else { // wait without timeout
342 schoenebeck 3290 dmsg(7,("Condition::WaitIfInternal() -> waiting for 'true' condition\n"));
343 schoenebeck 63 pthread_cond_wait(&__posix_true_condition, &__posix_mutex);
344 schoenebeck 3290 dmsg(7,("Condition::WaitIfInternal() -> awakened from 'true' condition waiting\n"));
345 schoenebeck 63 }
346 senoner 1481 #endif
347 schoenebeck 53 }
348     }
349 persson 2324 #ifndef WIN32
350     pthread_cleanup_pop(0);
351     #endif
352 schoenebeck 53 return res;
353     }
354    
355 schoenebeck 3290 int Condition::WaitIf(bool bCondition, long TimeoutSeconds, long TimeoutNanoSeconds) {
356     return WaitIfInternal(true/*do lock*/, bCondition, TimeoutSeconds, TimeoutNanoSeconds);
357     }
358    
359     int Condition::PreLockedWaitIf(bool bCondition, long TimeoutSeconds, long TimeoutNanoSeconds) {
360     return WaitIfInternal(false/*don't lock*/, bCondition, TimeoutSeconds, TimeoutNanoSeconds);
361     }
362    
363 schoenebeck 53 int Condition::WaitAndUnlockIf(bool bCondition, long TimeoutSeconds, long TimeoutNanoSeconds) {
364 schoenebeck 3290 int res = WaitIfInternal(true/*do lock*/, bCondition, TimeoutSeconds, TimeoutNanoSeconds);
365 schoenebeck 63 dmsg(7,("Condition::WaitAndUnlockIf() -> UNLOCK()\n"));
366 schoenebeck 53 Unlock();
367 schoenebeck 63 dmsg(7,("Condition::WaitAndUnlockIf() -> UNLOCK() passed\n"));
368 schoenebeck 53 return res;
369     }
370    
371 schoenebeck 3290 int Condition::PreLockedWaitAndUnlockIf(bool bCondition, long TimeoutSeconds, long TimeoutNanoSeconds) {
372     int res = WaitIfInternal(false/*don't lock*/, bCondition, TimeoutSeconds, TimeoutNanoSeconds);
373     dmsg(7,("Condition::PreLockedWaitAndUnlockIf() -> UNLOCK()\n"));
374     Unlock();
375     dmsg(7,("Condition::PreLockedWaitAndUnlockIf() -> UNLOCK() passed\n"));
376     return res;
377     }
378    
379     void Condition::SetInternal(bool bLock, bool bCondition) {
380 schoenebeck 63 dmsg(7,("Condition::Set() -> LOCK()\n"));
381 schoenebeck 3290 LockGuard lock = (bLock) ? LockGuard(*this) : LockGuard();
382 schoenebeck 63 dmsg(7,("Condition::Set() -> LOCK() passed\n"));
383 schoenebeck 53 if (this->bCondition != bCondition) {
384     this->bCondition = bCondition;
385 schoenebeck 63 if (bCondition) {
386     dmsg(7,("Condition::Set() -> broadcasting 'true' condition\n"));
387 senoner 1481 #if defined(WIN32)
388 senoner 1561 ConditionInternal::win32thread_cond_broadcast(&__win32_true_condition);
389 senoner 1481 #else
390 schoenebeck 63 pthread_cond_broadcast(&__posix_true_condition);
391 senoner 1481 #endif
392 schoenebeck 63 }
393     else {
394     dmsg(7,("Condition::Set() -> broadcasting 'false' condition\n"));
395 senoner 1481 #if defined(WIN32)
396 senoner 1561 ConditionInternal::win32thread_cond_broadcast(&__win32_false_condition);
397 senoner 1481 #else
398 schoenebeck 63 pthread_cond_broadcast(&__posix_false_condition);
399 senoner 1481 #endif
400 schoenebeck 63 }
401 schoenebeck 53 }
402     }
403 schoenebeck 880
404 schoenebeck 3290 void Condition::Set(bool bCondition) {
405     SetInternal(true/*do lock*/, bCondition);
406     }
407    
408     void Condition::PreLockedSet(bool bCondition) {
409     SetInternal(false/*don't lock*/, bCondition);
410     }
411    
412 schoenebeck 1221 bool Condition::GetUnsafe() {
413     return bCondition;
414     }
415    
416 persson 2316 #ifdef WIN32
417     void Condition::Reset() {
418     __win32_true_condition.waiters_count_ = 0;
419     __win32_true_condition.was_broadcast_ = 0;
420     __win32_false_condition.waiters_count_ = 0;
421     __win32_false_condition.was_broadcast_ = 0;
422     }
423     #endif
424    
425 schoenebeck 880 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC