LMMS
Loading...
Searching...
No Matches
RemotePluginBase.h
Go to the documentation of this file.
1/*
2 * RemotePluginBase.h - base class providing RPC like mechanisms
3 *
4 * Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
5 *
6 * This file is part of LMMS - https://lmms.io
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (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 GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program (see COPYING); if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301 USA.
22 *
23 */
24
25#ifndef LMMS_REMOTE_PLUGIN_BASE_H
26#define LMMS_REMOTE_PLUGIN_BASE_H
27
28#include <atomic> // IWYU pragma: keep
29#include <vector>
30#include <cstdio>
31#include <cstdlib>
32#include <cstring>
33#include <string>
34
35#include "lmmsconfig.h"
36
37#if !(defined(LMMS_HAVE_SYS_IPC_H) && defined(LMMS_HAVE_SEMAPHORE_H))
38#define SYNC_WITH_SHM_FIFO
39
40#ifdef LMMS_HAVE_PROCESS_H
41#include <process.h>
42#endif
43#else // !(LMMS_HAVE_SYS_IPC_H && LMMS_HAVE_SEMAPHORE_H)
44#ifdef LMMS_HAVE_UNISTD_H
45#include <unistd.h>
46#endif
47#endif // !(LMMS_HAVE_SYS_IPC_H && LMMS_HAVE_SEMAPHORE_H)
48
49#ifdef LMMS_HAVE_LOCALE_H
50#include <clocale> // IWYU pragma: keep
51#endif
52
53#ifdef LMMS_HAVE_PTHREAD_H
54#include <pthread.h>
55#endif
56
57
58#ifdef BUILD_REMOTE_PLUGIN_CLIENT
59#undef LMMS_EXPORT
60#define LMMS_EXPORT
61
62#ifndef SYNC_WITH_SHM_FIFO
63#include <sys/socket.h>
64#include <sys/un.h>
65#endif // SYNC_WITH_SHM_FIFO
66
67#else // BUILD_REMOTE_PLUGIN_CLIENT
68#include "lmms_export.h"
69#include <QString>
70
71#ifndef SYNC_WITH_SHM_FIFO
72#include <poll.h>
73#include <unistd.h> // IWYU pragma: keep
74#endif // SYNC_WITH_SHM_FIFO
75
76#endif // BUILD_REMOTE_PLUGIN_CLIENT
77
78#ifdef SYNC_WITH_SHM_FIFO
79#include "SharedMemory.h"
80#include "SystemSemaphore.h"
81#endif
82
83namespace lmms
84{
85
86
87#ifdef SYNC_WITH_SHM_FIFO
88
89
90// sometimes we need to exchange bigger messages (e.g. for VST parameter dumps)
91// so set a usable value here
92const int SHM_FIFO_SIZE = 512*1024;
93
94
95// implements a FIFO inside a shared memory segment
96class shmFifo
97{
98 // need this union to handle different sizes of sem_t on 32 bit
99 // and 64 bit platforms
100 union sem32_t
101 {
102 int semKey;
103 char fill[32];
104 } ;
105 struct shmData
106 {
107 sem32_t dataSem; // semaphore for locking this
108 // FIFO management data
109 sem32_t messageSem; // semaphore for incoming messages
110 int32_t startPtr; // current start of FIFO in memory
111 int32_t endPtr; // current end of FIFO in memory
112 char data[SHM_FIFO_SIZE]; // actual data
113 } ;
114
115public:
116#ifndef BUILD_REMOTE_PLUGIN_CLIENT
117 // constructor for master-side
118 shmFifo() :
119 m_invalid( false ),
120 m_master( true ),
121 m_lockDepth( 0 )
122 {
123 m_data.create();
124 m_data->startPtr = m_data->endPtr = 0;
125 static int k = 0;
126 m_data->dataSem.semKey = ( getpid()<<10 ) + ++k;
127 m_data->messageSem.semKey = ( getpid()<<10 ) + ++k;
128 m_dataSem = SystemSemaphore{std::to_string(m_data->dataSem.semKey), 1u};
129 m_messageSem = SystemSemaphore{std::to_string(m_data->messageSem.semKey), 0u};
130 }
131#endif
132
133 // constructor for remote-/client-side - use _shm_key for making up
134 // the connection to master
135 shmFifo(const std::string& shmKey) :
136 m_invalid( false ),
137 m_master( false ),
138 m_lockDepth( 0 )
139 {
140 m_data.attach(shmKey);
141 m_dataSem = SystemSemaphore{std::to_string(m_data->dataSem.semKey)};
142 m_messageSem = SystemSemaphore{std::to_string(m_data->messageSem.semKey)};
143 }
144
145 inline bool isInvalid() const
146 {
147 return m_invalid;
148 }
149
150 void invalidate()
151 {
152 m_invalid = true;
153 }
154
155 // do we act as master (i.e. not as remote-process?)
156 inline bool isMaster() const
157 {
158 return m_master;
159 }
160
161 // recursive lock
162 inline void lock()
163 {
164 if( !isInvalid() && m_lockDepth.fetch_add( 1 ) == 0 )
165 {
166 m_dataSem.acquire();
167 }
168 }
169
170 // recursive unlock
171 inline void unlock()
172 {
173 if( m_lockDepth.fetch_sub( 1 ) <= 1 )
174 {
175 m_dataSem.release();
176 }
177 }
178
179 // wait until message-semaphore is available
180 inline void waitForMessage()
181 {
182 if( !isInvalid() )
183 {
184 m_messageSem.acquire();
185 }
186 }
187
188 // increase message-semaphore
189 inline void messageSent()
190 {
191 m_messageSem.release();
192 }
193
194
195 inline int32_t readInt()
196 {
197 int32_t i;
198 read( &i, sizeof( i ) );
199 return i;
200 }
201
202 inline void writeInt( const int32_t & _i )
203 {
204 write( &_i, sizeof( _i ) );
205 }
206
207 std::string readString()
208 {
209 std::string ret;
210 const int len = readInt();
211 if (len > 0)
212 {
213 ret.resize(static_cast<std::size_t>(len));
214 read(ret.data(), len);
215 ret[len] = '\0';
216 }
217 return ret;
218 }
219
220
221 inline void writeString( const std::string & _s )
222 {
223 const int len = _s.size();
224 writeInt( len );
225 write( _s.c_str(), len );
226 }
227
228
229 inline bool messagesLeft()
230 {
231 if( isInvalid() )
232 {
233 return false;
234 }
235 lock();
236 const bool empty = ( m_data->startPtr == m_data->endPtr );
237 unlock();
238 return !empty;
239 }
240
241
242 const std::string& shmKey() const
243 {
244 return m_data.key();
245 }
246
247
248private:
249 void read( void * _buf, int _len )
250 {
251 if( isInvalid() )
252 {
253 memset( _buf, 0, _len );
254 return;
255 }
256 lock();
257 while( isInvalid() == false &&
258 _len > m_data->endPtr - m_data->startPtr )
259 {
260 unlock();
261#ifndef LMMS_BUILD_WIN32
262 usleep( 5 );
263#endif
264 lock();
265 }
266 std::memcpy(_buf, m_data->data + m_data->startPtr, _len);
267 m_data->startPtr += _len;
268 // nothing left?
269 if( m_data->startPtr == m_data->endPtr )
270 {
271 // then reset to 0
272 m_data->startPtr = m_data->endPtr = 0;
273 }
274 unlock();
275 }
276
277 void write( const void * _buf, int _len )
278 {
279 if( isInvalid() || _len > SHM_FIFO_SIZE )
280 {
281 return;
282 }
283 lock();
284 while( _len > SHM_FIFO_SIZE - m_data->endPtr )
285 {
286 // if no space is left, try to move data to front
287 if( m_data->startPtr > 0 )
288 {
289 memmove( m_data->data,
290 m_data->data + m_data->startPtr,
291 m_data->endPtr - m_data->startPtr );
292 m_data->endPtr = m_data->endPtr -
293 m_data->startPtr;
294 m_data->startPtr = 0;
295 }
296 unlock();
297#ifndef LMMS_BUILD_WIN32
298 usleep( 5 );
299#endif
300 lock();
301 }
302 std::memcpy(m_data->data + m_data->endPtr, _buf, _len);
303 m_data->endPtr += _len;
304 unlock();
305 }
306
307 volatile bool m_invalid;
308 bool m_master;
309 SharedMemory<shmData> m_data;
310 SystemSemaphore m_dataSem;
311 SystemSemaphore m_messageSem;
312 std::atomic_int m_lockDepth;
313};
314#endif // SYNC_WITH_SHM_FIFO
315
316
317
349
350
351
352class LMMS_EXPORT RemotePluginBase
353{
354public:
355 struct message
356 {
358 id( IdUndefined ),
359 data()
360 {
361 }
362
363 message( const message & _m ) = default;
364
365 message( int _id ) :
366 id( _id ),
367 data()
368 {
369 }
370
371 template<class... Args>
372 message& addString(Args&&... args)
373 {
374 data.emplace_back(std::forward<Args>(args)...);
375 return *this;
376 }
377
378 message & addInt( int _i )
379 {
380 char buf[32];
381 std::snprintf(buf, 32, "%d", _i);
382 data.emplace_back( buf );
383 return *this;
384 }
385
386 message & addFloat( float _f )
387 {
388 char buf[32];
389 std::snprintf(buf, 32, "%f", _f);
390 data.emplace_back( buf );
391 return *this;
392 }
393
394 inline std::string getString( int _p = 0 ) const
395 {
396 return data[_p];
397 }
398
399#ifndef BUILD_REMOTE_PLUGIN_CLIENT
400 inline QString getQString( int _p = 0 ) const
401 {
402 return QString::fromStdString( getString( _p ) );
403 }
404#endif
405
406 inline int getInt( int _p = 0 ) const
407 {
408 return atoi( data[_p].c_str() );
409 }
410
411 inline float getFloat( int _p ) const
412 {
413 return (float) atof( data[_p].c_str() );
414 }
415
416 inline bool operator==( const message & _m ) const
417 {
418 return( id == _m.id );
419 }
420
421 int id;
422
423 private:
424 std::vector<std::string> data;
425
426 friend class RemotePluginBase;
427
428 } ;
429
430#ifdef SYNC_WITH_SHM_FIFO
431 RemotePluginBase( shmFifo * _in, shmFifo * _out );
432#else
434#endif
436
437#ifdef SYNC_WITH_SHM_FIFO
438 void reset( shmFifo *in, shmFifo *out )
439 {
440 delete m_in;
441 delete m_out;
442 m_in = in;
443 m_out = out;
444 }
445#endif
446
447 int sendMessage( const message & _m );
449
450 inline bool isInvalid() const
451 {
452#ifdef SYNC_WITH_SHM_FIFO
453 return m_in->isInvalid() || m_out->isInvalid();
454#else
455 return m_invalid;
456#endif
457 }
458
460 bool _busy_waiting = false );
461
463 {
465 processMessage( m );
466 return m;
467 }
468
469#ifndef SYNC_WITH_SHM_FIFO
471 {
472 int32_t i;
473 read( &i, sizeof( i ) );
474 return i;
475 }
476
477 inline void writeInt( const int32_t & _i )
478 {
479 write( &_i, sizeof( _i ) );
480 }
481
482 std::string readString()
483 {
484 std::string ret;
485 const int len = readInt();
486 if (len > 0)
487 {
488 ret.resize(static_cast<std::size_t>(len));
489 read(ret.data(), len);
490 ret[len] = '\0';
491 }
492 return ret;
493 }
494
495
496 inline void writeString( const std::string & _s )
497 {
498 const int len = _s.size();
499 writeInt( len );
500 write( _s.c_str(), len );
501 }
502#endif // SYNC_WITH_SHM_FIFO
503
504#ifndef BUILD_REMOTE_PLUGIN_CLIENT
505 inline bool messagesLeft()
506 {
507#ifdef SYNC_WITH_SHM_FIFO
508 return m_in->messagesLeft();
509#else
510 struct pollfd pollin;
511 pollin.fd = m_socket;
512 pollin.events = POLLIN;
513
514 if ( poll( &pollin, 1, 0 ) == -1 )
515 {
516 qWarning( "Unexpected poll error." );
517 }
518 return pollin.revents & POLLIN;
519#endif
520 }
521
523 {
524 while( messagesLeft() )
525 {
527 }
528 }
529
531 {
532 return waitDepthCounter() > 0;
533 }
534#endif // BUILD_REMOTE_PLUGIN_CLIENT
535
536 virtual bool processMessage( const message & _m ) = 0;
537
538
539protected:
540#ifdef SYNC_WITH_SHM_FIFO
541 inline const shmFifo * in() const
542 {
543 return m_in;
544 }
545
546 inline const shmFifo * out() const
547 {
548 return m_out;
549 }
550#endif
551
552 inline void invalidate()
553 {
554#ifdef SYNC_WITH_SHM_FIFO
555 m_in->invalidate();
556 m_out->invalidate();
557 m_in->messageSent();
558#else
559 m_invalid = true;
560#endif
561 }
562
563
564#ifndef SYNC_WITH_SHM_FIFO
566#endif
567
568
569private:
570#ifndef BUILD_REMOTE_PLUGIN_CLIENT
571 static int & waitDepthCounter()
572 {
573 static int waitDepth = 0;
574 return waitDepth;
575 }
576#endif
577
578#ifdef SYNC_WITH_SHM_FIFO
579 shmFifo * m_in;
580 shmFifo * m_out;
581#else
582 void read( void * _buf, int _len )
583 {
584 if( isInvalid() )
585 {
586 memset( _buf, 0, _len );
587 return;
588 }
589 char * buf = (char *) _buf;
590 int remaining = _len;
591 while ( remaining )
592 {
593 ssize_t nread = ::read( m_socket, buf, remaining );
594 switch ( nread )
595 {
596 case -1:
597 fprintf( stderr,
598 "Error while reading.\n" );
599 case 0:
600 invalidate();
601 memset( _buf, 0, _len );
602 return;
603 }
604 buf += nread;
605 remaining -= nread;
606 }
607 }
608
609 void write( const void * _buf, int _len )
610 {
611 if( isInvalid() )
612 {
613 return;
614 }
615 const char * buf = (const char *) _buf;
616 int remaining = _len;
617 while ( remaining )
618 {
619 ssize_t nwritten = ::write( m_socket, buf, remaining );
620 switch ( nwritten )
621 {
622 case -1:
623 fprintf( stderr,
624 "Error while writing.\n" );
625 case 0:
626 invalidate();
627 return;
628 }
629 buf += nwritten;
630 remaining -= nwritten;
631 }
632 }
633
634
636
637 pthread_mutex_t m_receiveMutex;
638 pthread_mutex_t m_sendMutex;
639#endif // SYNC_WITH_SHM_FIFO
640
641} ;
642
643} // namespace lmms
644
645#endif // LMMS_REMOTE_PLUGIN_BASE_H
static bool isMainThreadWaiting()
Definition RemotePluginBase.h:530
int sendMessage(const message &_m)
int32_t readInt()
Definition RemotePluginBase.h:470
int m_socket
Definition RemotePluginBase.h:565
void invalidate()
Definition RemotePluginBase.h:552
bool m_invalid
Definition RemotePluginBase.h:635
message fetchAndProcessNextMessage()
Definition RemotePluginBase.h:462
virtual bool processMessage(const message &_m)=0
bool messagesLeft()
Definition RemotePluginBase.h:505
void write(const void *_buf, int _len)
Definition RemotePluginBase.h:609
void writeString(const std::string &_s)
Definition RemotePluginBase.h:496
pthread_mutex_t m_receiveMutex
Definition RemotePluginBase.h:637
void fetchAndProcessAllMessages()
Definition RemotePluginBase.h:522
static int & waitDepthCounter()
Definition RemotePluginBase.h:571
pthread_mutex_t m_sendMutex
Definition RemotePluginBase.h:638
void read(void *_buf, int _len)
Definition RemotePluginBase.h:582
void writeInt(const int32_t &_i)
Definition RemotePluginBase.h:477
bool isInvalid() const
Definition RemotePluginBase.h:450
message waitForMessage(const message &_m, bool _busy_waiting=false)
std::string readString()
Definition RemotePluginBase.h:482
unsigned * m
Definition inflate.c:1559
register unsigned k
Definition inflate.c:946
struct huft * u[BMAX]
Definition inflate.c:1583
register unsigned i
Definition inflate.c:1575
JSAMPIMAGE data
Definition jpeglib.h:945
float in
Definition lilv_test.c:1460
float out
Definition lilv_test.c:1461
int int32_t
Definition mid.cpp:97
void fill(Buf &buf, T value)
Definition buffer.h:50
Definition AudioAlsa.cpp:35
RemoteMessageIDs
Definition RemotePluginBase.h:319
@ IdSaveSettingsToString
Definition RemotePluginBase.h:339
@ IdDebugMessage
Definition RemotePluginBase.h:345
@ IdSyncKey
Definition RemotePluginBase.h:324
@ IdMidiEvent
Definition RemotePluginBase.h:328
@ IdBufferSizeInformation
Definition RemotePluginBase.h:326
@ IdChangeInputOutputCount
Definition RemotePluginBase.h:334
@ IdLoadSettingsFromString
Definition RemotePluginBase.h:341
@ IdStartProcessing
Definition RemotePluginBase.h:329
@ IdChangeSharedMemoryKey
Definition RemotePluginBase.h:331
@ IdProcessingDone
Definition RemotePluginBase.h:330
@ IdInitDone
Definition RemotePluginBase.h:322
@ IdToggleUI
Definition RemotePluginBase.h:337
@ IdSavePresetFile
Definition RemotePluginBase.h:343
@ IdUndefined
Definition RemotePluginBase.h:320
@ IdLoadPresetFile
Definition RemotePluginBase.h:344
@ IdIdle
Definition RemotePluginBase.h:346
@ IdSaveSettingsToFile
Definition RemotePluginBase.h:340
@ IdHostInfoGotten
Definition RemotePluginBase.h:321
@ IdChangeOutputCount
Definition RemotePluginBase.h:333
@ IdQuit
Definition RemotePluginBase.h:323
@ IdIsUIVisible
Definition RemotePluginBase.h:338
@ IdHideUI
Definition RemotePluginBase.h:336
@ IdLoadSettingsFromFile
Definition RemotePluginBase.h:342
@ IdInformationUpdated
Definition RemotePluginBase.h:327
@ IdSampleRateInformation
Definition RemotePluginBase.h:325
@ IdUserBase
Definition RemotePluginBase.h:347
@ IdShowUI
Definition RemotePluginBase.h:335
@ IdChangeInputCount
Definition RemotePluginBase.h:332
#define true
Definition ordinals.h:82
#define false
Definition ordinals.h:83
Definition RemotePluginBase.h:356
message(const message &_m)=default
message()
Definition RemotePluginBase.h:357
message & addString(Args &&... args)
Definition RemotePluginBase.h:372
std::vector< std::string > data
Definition RemotePluginBase.h:424
message & addFloat(float _f)
Definition RemotePluginBase.h:386
QString getQString(int _p=0) const
Definition RemotePluginBase.h:400
std::string getString(int _p=0) const
Definition RemotePluginBase.h:394
bool operator==(const message &_m) const
Definition RemotePluginBase.h:416
int getInt(int _p=0) const
Definition RemotePluginBase.h:406
int id
Definition RemotePluginBase.h:421
friend class RemotePluginBase
Definition RemotePluginBase.h:426
message & addInt(int _i)
Definition RemotePluginBase.h:378
float getFloat(int _p) const
Definition RemotePluginBase.h:411
message(int _id)
Definition RemotePluginBase.h:365
read(f, &c, 1)