diff foosdk/sdk/foobar2000/helpers/CmdThread.h @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foosdk/sdk/foobar2000/helpers/CmdThread.h	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,136 @@
+#pragma once
+#include <pfc/wait_queue.h>
+#include <pfc/pool.h>
+#include <pfc/threads.h>
+#include <functional>
+#include "rethrow.h"
+#include <pfc/timers.h>
+
+namespace ThreadUtils {
+
+    typedef std::function<bool(pfc::eventHandle_t, double) > waitFunc_t;
+
+	// Serialize access to some resource to a single thread
+	// Execute blocking/nonabortable methods in with proper abortability (detach on abort and move on)
+	class cmdThread {
+	public:
+		typedef std::function<void () > func_t;
+		typedef pfc::waitQueue<func_t> queue_t;
+		typedef std::function<void (abort_callback&) > funcAbortable_t;
+        
+    protected:
+        std::function<void () > makeWorker(waitFunc_t wf = pfc::event::g_wait_for) {
+            auto q = m_queue;
+            auto x = m_atExit;
+            return [q, x, wf] {
+                for ( ;; ) {
+                    func_t f;
+                    wf(q->get_event_handle(), -1);
+                    if (!q->get(f)) break;
+                    try { f(); } catch(...) {}
+                }
+                // No guard for atExit access, as nobody is supposed to be still able to call host object methods by the time we get here
+                for( auto i = x->begin(); i != x->end(); ++ i ) {
+                    auto f = *i;
+                    try { f(); } catch(...) {}
+                }
+            };
+        };
+        std::function<void () > makeWorker2( std::function<void()> updater, double interval, waitFunc_t wf = pfc::event::g_wait_for) {
+            auto q = m_queue;
+            auto x = m_atExit;
+            return [=] {
+                pfc::lores_timer t; t.start();
+                for ( ;; ) {
+                    {
+                        bool bWorkReady = false;
+                        double left = interval - t.query();
+                        if ( left > 0 ) {
+                            if (wf(q->get_event_handle(), left)) bWorkReady = true;
+                        }
+                        
+                        if (!bWorkReady) {
+                            updater();
+                            t.start();
+                            continue;
+                        }
+                    }
+                    
+                    func_t f;
+                    wf(q->get_event_handle(), -1);
+                    if (!q->get(f)) break;
+                    try { f(); } catch(...) {}
+                }
+                // No guard for atExit access, as nobody is supposed to be still able to call host object methods by the time we get here
+                for( auto i = x->begin(); i != x->end(); ++ i ) {
+                    auto f = *i;
+                    try { f(); } catch(...) {}
+                }
+            };
+        };
+        
+        // For derived classes: create new instance without starting thread, supply thread using by yourself
+        class noCreate {};
+        cmdThread( noCreate ) {}
+    public:
+
+		cmdThread() {
+			pfc::splitThread( makeWorker() );
+		}
+        
+		void atExit( func_t f ) {
+			m_atExit->push_back(f);
+		}
+		~cmdThread() {
+			m_queue->set_eof();
+		}
+        void runSynchronously( func_t f ) { runSynchronously_(f, nullptr); }
+        void runSynchronously_( func_t f, abort_callback * abortOrNull ) {
+            auto evt = m_eventPool.make();
+            evt->set_state(false);
+            auto rethrow = std::make_shared<ThreadUtils::CRethrow>();
+            auto worker2 = [f, rethrow, evt] {
+                rethrow->exec(f);
+                evt->set_state( true );
+            };
+
+            add ( worker2 );
+            
+            if ( abortOrNull != nullptr ) {
+                abortOrNull->waitForEvent( * evt, -1 );
+            } else {
+                evt->wait_for(-1);
+            }
+
+            m_eventPool.put( evt );
+
+            rethrow->rethrow();
+        }
+		void runSynchronously( func_t f, abort_callback & abort ) {
+            runSynchronously_(f, &abort);
+		}
+		void runSynchronously2( funcAbortable_t f, abort_callback & abort ) {
+			auto subAbort = m_abortPool.make();
+			subAbort->reset();
+			auto worker = [subAbort, f] {
+				f(*subAbort);
+			};
+
+			try {
+				runSynchronously( worker, abort );
+			} catch(...) {
+				subAbort->set(); throw;
+			}
+
+			m_abortPool.put( subAbort );
+		}
+
+		void add( func_t f ) { m_queue->put( f ); }
+	private:
+		pfc::objPool<pfc::event> m_eventPool;
+		pfc::objPool<abort_callback_impl> m_abortPool;
+		std::shared_ptr<queue_t> m_queue = std::make_shared<queue_t>();
+		typedef std::list<func_t> atExit_t;
+		std::shared_ptr<atExit_t> m_atExit = std::make_shared< atExit_t >();
+	};
+}