211 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  * Furnace Tracker - multi-system chiptune tracker
 | |
|  * Copyright (C) 2021-2024 tildearrow and contributors
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation; either version 2 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along
 | |
|  * with this program; if not, write to the Free Software Foundation, Inc.,
 | |
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | |
|  */
 | |
| 
 | |
| #include "workPool.h"
 | |
| #include "../ta-log.h"
 | |
| #include <thread>
 | |
| 
 | |
| void* _workThread(void* inst) {
 | |
|   ((DivWorkThread*)inst)->run();
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| void DivWorkThread::run() {
 | |
|   //std::unique_lock<std::mutex> unique(selfLock);
 | |
|   DivPendingTask task;
 | |
|   bool setFuckingPromise=false;
 | |
| 
 | |
|   logV("running work thread");
 | |
| 
 | |
|   while (true) {
 | |
|     lock.lock();
 | |
|     if (tasks.empty()) {
 | |
|       lock.unlock();
 | |
|       isBusy=false;
 | |
|       if (setFuckingPromise) {
 | |
|         parent->notify.set_value();
 | |
|         setFuckingPromise=false;
 | |
|         //std::this_thread::yield();
 | |
|       }
 | |
|       if (terminate) {
 | |
|         break;
 | |
|       }
 | |
|       std::future<void> future=notify.get_future();
 | |
|       future.wait();
 | |
|       lock.lock();
 | |
|       notify=std::promise<void>();
 | |
|       promiseAlreadySet=false;
 | |
|       lock.unlock();
 | |
|       continue;
 | |
|     } else {
 | |
|       task=tasks.front();
 | |
|       tasks.pop();
 | |
|       lock.unlock();
 | |
| 
 | |
|       task.func(task.funcArg);
 | |
| 
 | |
|       int busyCount=--parent->busyCount;
 | |
|       if (busyCount<0) {
 | |
|         logE("oh no PROBLEM...");
 | |
|       }
 | |
|       if (busyCount==0) {
 | |
|         setFuckingPromise=true;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool DivWorkThread::assign(void (*what)(void*), void* arg) {
 | |
|   lock.lock();
 | |
|   if (tasks.size()>=30) {
 | |
|     lock.unlock();
 | |
|     return false;
 | |
|   }
 | |
|   tasks.push(DivPendingTask(what,arg));
 | |
|   parent->busyCount++;
 | |
|   isBusy=true;
 | |
|   lock.unlock();
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void DivWorkThread::wait() {
 | |
|   if (!isBusy) return;
 | |
| }
 | |
| 
 | |
| bool DivWorkThread::busy() {
 | |
|   return isBusy;
 | |
| }
 | |
| 
 | |
| void DivWorkThread::finish() {
 | |
|   lock.lock();
 | |
|   terminate=true;
 | |
|   try {
 | |
|     notify.set_value();
 | |
|   } catch (std::future_error& e) {
 | |
|     logE("future error! beware!");
 | |
|   }
 | |
|   lock.unlock();
 | |
|   thread->join();
 | |
| }
 | |
| 
 | |
| bool DivWorkThread::init(DivWorkPool* p) {
 | |
|   parent=p;
 | |
|   try {
 | |
|     thread=new std::thread(_workThread,this);
 | |
|   } catch (std::system_error& e) {
 | |
|     logE("could not start thread! %s",e.what());
 | |
|     thread=NULL;
 | |
|     return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void DivWorkPool::push(void (*what)(void*), void* arg) {
 | |
|   // if no work threads, just execute
 | |
|   if (!threaded) {
 | |
|     what(arg);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   for (unsigned int tryCount=0; tryCount<count; tryCount++) {
 | |
|     if (pos>=count) pos=0;
 | |
|     if (workThreads[pos++].assign(what,arg)) return;
 | |
|   }
 | |
| 
 | |
|   // all threads are busy
 | |
|   logW("DivWorkPool: all work threads busy!");
 | |
|   what(arg);
 | |
| }
 | |
| 
 | |
| bool DivWorkPool::busy() {
 | |
|   if (!threaded) return false;
 | |
|   for (unsigned int i=0; i<count; i++) {
 | |
|     if (workThreads[i].busy()) return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void DivWorkPool::wait() {
 | |
|   if (!threaded) return;
 | |
| 
 | |
|   if (busyCount==0) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   std::future<void> future=notify.get_future();
 | |
| 
 | |
|   // start running
 | |
|   for (unsigned int i=0; i<count; i++) {
 | |
|     if (!workThreads[i].promiseAlreadySet && !workThreads[i].tasks.empty()) {
 | |
|       try {
 | |
|         workThreads[i].lock.lock();
 | |
|         workThreads[i].promiseAlreadySet=true;
 | |
|         workThreads[i].notify.set_value();
 | |
|         workThreads[i].lock.unlock();
 | |
|       } catch (std::exception& e) {
 | |
|         logE("ERROR IN THREAD SYNC! %s",e.what());
 | |
|         abort();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   //std::this_thread::yield();
 | |
| 
 | |
|   // wait
 | |
|   future.wait();
 | |
| 
 | |
|   notify=std::promise<void>();
 | |
| 
 | |
|   pos=0;
 | |
| }
 | |
| 
 | |
| DivWorkPool::DivWorkPool(unsigned int threads):
 | |
|   threaded(threads>0),
 | |
|   count(threads),
 | |
|   pos(0),
 | |
|   busyCount(0) {
 | |
|   if (threaded) {
 | |
|     workThreads=new DivWorkThread[threads];
 | |
|     for (unsigned int i=0; i<count; i++) {
 | |
|       if (!workThreads[i].init(this)) { 
 | |
|         count=i;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     if (count<=0) {
 | |
|       logE("DivWorkPool: couldn't start any threads! falling back to non-threaded mode.");
 | |
|       delete[] workThreads;
 | |
|       threaded=false;
 | |
|       workThreads=NULL;
 | |
|     }
 | |
|   } else {
 | |
|     workThreads=NULL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| DivWorkPool::~DivWorkPool() {
 | |
|   if (threaded) {
 | |
|     if (workThreads!=NULL) {
 | |
|       for (unsigned int i=0; i<count; i++) {
 | |
|         workThreads[i].finish();
 | |
|       }
 | |
|       delete[] workThreads;
 | |
|     }
 | |
|   }
 | |
| }
 | 
