/* * Deskflow -- mouse and keyboard sharing utility * SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd. * SPDX-FileCopyrightText: (C) 2004 Chris Schoeneman * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception */ #pragma once #include "arch/IArchNetwork.h" #include #include template class CondVar; class Mutex; class Thread; class ISocket; class ISocketMultiplexerJob; //! Socket multiplexer /*! A socket multiplexer services multiple sockets simultaneously. */ class SocketMultiplexer { public: SocketMultiplexer(); SocketMultiplexer(SocketMultiplexer const &) = delete; SocketMultiplexer(SocketMultiplexer &&) = delete; ~SocketMultiplexer(); SocketMultiplexer &operator=(SocketMultiplexer const &) = delete; SocketMultiplexer &operator=(SocketMultiplexer &&) = delete; //! @name manipulators //@{ void addSocket(ISocket *, ISocketMultiplexerJob *); void removeSocket(ISocket *); //@} //! @name accessors //@{ // maybe belongs on ISocketMultiplexer static SocketMultiplexer *getInstance(); //@} private: // list of jobs. we use a list so we can safely iterate over it // while other threads modify it. using SocketJobs = std::list; using JobCursor = SocketJobs::iterator; using SocketJobMap = std::map; // service sockets. the service thread will only access m_sockets // and m_update while m_pollable and m_polling are true. all other // threads must only modify these when m_pollable and m_polling are // false. only the service thread sets m_polling. [[noreturn]] void serviceThread(void *); // create, iterate, and destroy a cursor. a cursor is used to // safely iterate through the job list while other threads modify // the list. it works by inserting a dummy item in the list and // moving that item through the list. the dummy item will never // be removed by other edits so an iterator pointing at the item // remains valid until we remove the dummy item in deleteCursor(). // nextCursor() finds the next non-dummy item, moves our dummy // item just past it, and returns an iterator for the non-dummy // item. all cursor calls lock the mutex for their duration. JobCursor newCursor(); JobCursor nextCursor(JobCursor); void deleteCursor(JobCursor); // lock out locking the job list. this blocks if another thread // has already locked out locking. once it returns, only the // calling thread will be able to lock the job list after any // current lock is released. void lockJobListLock(); // lock the job list. this blocks if the job list is already // locked. the calling thread must have called requestJobLock. void lockJobList(); // unlock the job list and the lock out on locking. void unlockJobList(); private: Mutex *m_mutex = nullptr; Thread *m_thread = nullptr; bool m_update = false; CondVar *m_jobsReady = nullptr; CondVar *m_jobListLock = nullptr; CondVar *m_jobListLockLocked = nullptr; Thread *m_jobListLocker = nullptr; Thread *m_jobListLockLocker = nullptr; SocketJobs m_socketJobs = {}; SocketJobMap m_socketJobMap = {}; ISocketMultiplexerJob *m_cursorMark = nullptr; };