mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-07-11 22:03:01 -06:00
bazaar: CoWork clone with own Thread pool, AddThread,KillThread feature, WorkQueue a one-thread threadpool to offload work to be processed in order (see WorkQueueTest & WorkQueueTest2)
git-svn-id: svn://ultimatepp.org/upp/trunk@2624 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
e8a2be07ec
commit
00369e6875
20 changed files with 813 additions and 0 deletions
208
bazaar/CoWork/CoWork.cpp
Normal file
208
bazaar/CoWork/CoWork.cpp
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
#include "CoWork.h"
|
||||
|
||||
namespace MyCoWork
|
||||
{
|
||||
|
||||
#ifdef _MULTITHREADED
|
||||
|
||||
#define LLOG(x) // LOG(x)
|
||||
#define LDUMP(x) // DUMP(x)
|
||||
|
||||
CoWork::Pool& CoWork::pool()
|
||||
{
|
||||
static Pool pool;
|
||||
return pool;
|
||||
}
|
||||
|
||||
void CoWork::Pool::AddThread(int count)
|
||||
{
|
||||
lock.Enter();
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
Thread * pt = new Thread();
|
||||
unsigned key = GetPtrHashValue(pt);
|
||||
threads.Add(key, pt).Run(THISBACK1(ThreadRun, key));
|
||||
}
|
||||
lock.Leave();
|
||||
}
|
||||
|
||||
void CoWork::Pool::KillThread(int count, bool waitone)
|
||||
{
|
||||
MJob job;
|
||||
job.work = NULL;
|
||||
bool finishall = false;
|
||||
|
||||
lock.Enter();
|
||||
if(count < 0)
|
||||
{
|
||||
finishall = true;
|
||||
jobs.Clear();
|
||||
count = threads.GetCount();
|
||||
}
|
||||
count = min(count, threads.GetCount()); //clip
|
||||
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
jobs.Add(job);
|
||||
waitforjob.Release(); //threads will consume their Thread instances
|
||||
}
|
||||
lock.Leave();
|
||||
|
||||
//wait only when finishing
|
||||
//scan the threads to find quited ones, if none available, handle over
|
||||
if(waitone || finishall)
|
||||
{
|
||||
Sleep(1); //give chance for some to have finished, the waiting at least
|
||||
lock.Enter();
|
||||
int i = 0;
|
||||
while(count > 0)
|
||||
{
|
||||
if(threads.GetKey(i) != 0)
|
||||
{
|
||||
//a used thread, check next
|
||||
if(++i >= threads.GetCount())
|
||||
{
|
||||
i=0;
|
||||
lock.Leave();
|
||||
Sleep(1); //give chance for some to have finished
|
||||
lock.Enter();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
//a quited thread
|
||||
Thread * pt = threads.Detach(i);
|
||||
--count;
|
||||
if(i >= threads.GetCount()) i=0;
|
||||
|
||||
lock.Leave();
|
||||
|
||||
//finish a Thread context
|
||||
pt->Wait();
|
||||
delete pt;
|
||||
pt = NULL;
|
||||
|
||||
lock.Enter();
|
||||
}
|
||||
lock.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
CoWork::Pool::Pool(int threadnr)
|
||||
{
|
||||
LLOG("CoWork INIT with threadnr " << threadnr);
|
||||
if(threadnr < 0)
|
||||
threadnr = CPU_Cores() + 2;
|
||||
AddThread(threadnr);
|
||||
}
|
||||
|
||||
CoWork::Pool::~Pool()
|
||||
{
|
||||
LLOG("Quit");
|
||||
KillThread(-1); //all
|
||||
LLOG("Quit ended");
|
||||
}
|
||||
|
||||
bool CoWork::Pool::DoJob()
|
||||
{
|
||||
MJob job = jobs.Pop();
|
||||
if(job.work == NULL) {
|
||||
RLOG("Quit thread initiate");
|
||||
return true;
|
||||
}
|
||||
lock.Leave();
|
||||
job.cb();
|
||||
lock.Enter();
|
||||
if(--job.work->todo <= 0) {
|
||||
LLOG("Releasing waitforfinish of (CoWork " << FormatIntHex(job.work) << ")");
|
||||
job.work->waitforfinish.Release(); //multiple call, but semaphore cant become negative anyway
|
||||
}
|
||||
LLOG("Finished, remaining todo " << job.work->todo << " (CoWork " << FormatIntHex(job.work) << ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
void CoWork::Pool::ThreadRun(unsigned tno)
|
||||
{
|
||||
LLOG("CoWork thread #" << tno << " started");
|
||||
lock.Enter();
|
||||
for(;;) {
|
||||
while(jobs.GetCount() <= 0) {
|
||||
waiting_threads++;
|
||||
lock.Leave();
|
||||
LLOG("#" << tno << " Waiting for job");
|
||||
waitforjob.Wait();
|
||||
LLOG("#" << tno << " Waiting ended");
|
||||
lock.Enter();
|
||||
}
|
||||
LLOG("#" << tno << " Job acquired");
|
||||
if(DoJob())
|
||||
break;
|
||||
LLOG("#" << tno << " Job finished");
|
||||
}
|
||||
int id = threads.Find(tno);
|
||||
Thread & t = threads[id];
|
||||
|
||||
threads.SetKey(id, 0); //mark as quited
|
||||
lock.Leave();
|
||||
RLOG("!!! CoWork thread #" << tno << " finished");
|
||||
}
|
||||
|
||||
void CoWork::Do(Callback cb) {
|
||||
Pool& p = _pool;
|
||||
p.lock.Enter();
|
||||
if(p.jobs.GetCount() > 128) {
|
||||
LLOG("Stack full: running in the main thread");
|
||||
p.lock.Leave();
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
MJob job;
|
||||
job.cb = cb;
|
||||
job.work = this;
|
||||
p.jobs.Add(job);
|
||||
todo++;
|
||||
LLOG("Adding job; todo: " << todo << " (CoWork " << FormatIntHex(this) << ")");
|
||||
if(p.waiting_threads>0) {
|
||||
LLOG("Releasing thread waiting for job: " << p.waiting_threads);
|
||||
p.waiting_threads--;
|
||||
p.waitforjob.Release();
|
||||
}
|
||||
p.lock.Leave();
|
||||
}
|
||||
|
||||
void CoWork::Finish() {
|
||||
#ifdef _MULTITHREADED
|
||||
Pool &p = _pool;
|
||||
p.lock.Enter();
|
||||
while(todo>0) {
|
||||
LLOG("Finish: todo: " << todo << " (CoWork " << FormatIntHex(this) << ")");
|
||||
if(p.jobs.GetCount()>0)
|
||||
p.DoJob();
|
||||
else {
|
||||
p.lock.Leave();
|
||||
LLOG("WaitForFinish (CoWork " << FormatIntHex(this) << ")");
|
||||
waitforfinish.Wait();
|
||||
p.lock.Enter();
|
||||
}
|
||||
}
|
||||
p.lock.Leave();
|
||||
LLOG("CoWork finished");
|
||||
#endif
|
||||
}
|
||||
|
||||
CoWork::CoWork(int threadnr)
|
||||
: _pool((threadnr<0)?(pool()):( _opool.Attach(new Pool(threadnr)),_opool.operator*()))
|
||||
{
|
||||
LLOG("*** CoWork constructed " << FormatHex(this));
|
||||
todo = 0;
|
||||
}
|
||||
|
||||
CoWork::~CoWork()
|
||||
{
|
||||
Finish();
|
||||
LLOG("~~~ CoWork destructed");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} //namespace
|
||||
85
bazaar/CoWork/CoWork.h
Normal file
85
bazaar/CoWork/CoWork.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#ifndef _CoWork_CoWork_h
|
||||
#define _CoWork_CoWork_h
|
||||
|
||||
#include <Core/Core.h>
|
||||
|
||||
using namespace Upp;
|
||||
|
||||
namespace MyCoWork
|
||||
{
|
||||
|
||||
#ifdef _MULTITHREADED
|
||||
|
||||
class CoWork : NoCopy {
|
||||
|
||||
struct MJob : Moveable<MJob> {
|
||||
Callback cb;
|
||||
CoWork *work;
|
||||
};
|
||||
|
||||
struct Pool {
|
||||
typedef Pool CLASSNAME;
|
||||
Vector<MJob> jobs;
|
||||
int waiting_threads;
|
||||
ArrayMap<unsigned, Thread> threads;
|
||||
|
||||
CriticalSection lock;
|
||||
Semaphore waitforjob;
|
||||
|
||||
Pool(int threadnr = -1);
|
||||
~Pool();
|
||||
|
||||
void AddThread(int count = 1);
|
||||
void KillThread(int count = 1, bool waitone = false);
|
||||
|
||||
bool DoJob();
|
||||
void ThreadRun(unsigned tno);
|
||||
};
|
||||
|
||||
friend struct Pool;
|
||||
|
||||
One<Pool> _opool; //local pool, needs to be here, before _pool, to be initialized before
|
||||
static Pool& pool(); //global pool, switch in CoWork()
|
||||
Pool & _pool; //this ref is actually used, set depending on threadnr
|
||||
|
||||
Semaphore waitforfinish;
|
||||
int todo;
|
||||
|
||||
public:
|
||||
void Do(Callback cb);
|
||||
CoWork& operator&(Callback cb) { Do(cb); return *this; }
|
||||
|
||||
int GetThreadCount() const { return _pool.threads.GetCount(); }
|
||||
int GetThreadCount() { _pool.lock.Enter(); int c = _pool.threads.GetCount(); _pool.lock.Leave(); return c; }
|
||||
void AddThread(int count = 1) { ASSERT(&_pool != &pool()); _pool.AddThread(count); }
|
||||
void KillThread(int count = 1, bool waitone = false) { ASSERT(&_pool != &pool()); _pool.KillThread(count, waitone); }
|
||||
|
||||
void Finish();
|
||||
|
||||
CoWork(int threadnr = -1);
|
||||
~CoWork();
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class CoWork : NoCopy {
|
||||
public:
|
||||
void Do(Callback cb) { cb(); }
|
||||
CoWork& operator&(Callback cb) { cb(); return *this; }
|
||||
void Finish() {}
|
||||
CoWork(int threadnr = -1) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class WorkQueue : public CoWork
|
||||
{
|
||||
public:
|
||||
WorkQueue()
|
||||
: CoWork(1)
|
||||
{}
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
||||
#endif
|
||||
9
bazaar/CoWork/CoWork.upp
Normal file
9
bazaar/CoWork/CoWork.upp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
description "the custom CoWork which lets specify thread count of a new pool and a WorkQueue\377";
|
||||
|
||||
uses
|
||||
Core;
|
||||
|
||||
file
|
||||
CoWork.h,
|
||||
CoWork.cpp;
|
||||
|
||||
1
bazaar/CoWork/Img.iml
Normal file
1
bazaar/CoWork/Img.iml
Normal file
|
|
@ -0,0 +1 @@
|
|||
PREMULTIPLIED
|
||||
4
bazaar/CoWork/init
Normal file
4
bazaar/CoWork/init
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#ifndef _CoWork_icpp_init_stub
|
||||
#define _CoWork_icpp_init_stub
|
||||
#include "Core/init"
|
||||
#endif
|
||||
178
bazaar/WorkQueue/WorkQueue.cpp
Normal file
178
bazaar/WorkQueue/WorkQueue.cpp
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
#include "WorkQueue.h"
|
||||
|
||||
namespace MyWQ
|
||||
{
|
||||
|
||||
#ifdef _MULTITHREADED
|
||||
|
||||
#define LLOG(x) // LOG(x)
|
||||
#define LDUMP(x) // DUMP(x)
|
||||
|
||||
WorkQueue::Pool& WorkQueue::pool()
|
||||
{
|
||||
// static Pool pool;
|
||||
return _pool;
|
||||
}
|
||||
|
||||
WorkQueue::Pool::Pool(int threadnr)
|
||||
: jobcount(0), lastjob(_jobs.GetPtr())
|
||||
{
|
||||
LLOG("WorkQueue INIT with threadnr " << threadnr);
|
||||
if(threadnr < 0)
|
||||
threadnr = CPU_Cores() + 2;
|
||||
for(int i = 0; i < threadnr; i++)
|
||||
threads.Add().Run(THISBACK1(ThreadRun, i));
|
||||
}
|
||||
|
||||
WorkQueue::Pool::~Pool()
|
||||
{
|
||||
LLOG("Quit");
|
||||
//MJob job;
|
||||
//job.work = NULL;
|
||||
lock.Enter();
|
||||
//jobs.Clear();
|
||||
_jobs.DeleteList();
|
||||
jobcount = 0;
|
||||
//jobs.Add(job);
|
||||
MJob & job = NextFree();
|
||||
lock.Leave();
|
||||
for(int i = 0; i < threads.GetCount(); i++)
|
||||
waitforjob.Release();
|
||||
for(int i = 0; i < threads.GetCount(); i++)
|
||||
threads[i].Wait();
|
||||
LLOG("Quit ended");
|
||||
}
|
||||
|
||||
bool WorkQueue::Pool::DoJob()
|
||||
{
|
||||
MJob * list = _jobs.GetPtr();
|
||||
MJob * e = list->GetNext();
|
||||
ASSERT(e != list);
|
||||
//if(jobs.Top().work == NULL) {
|
||||
if(e->work == NULL) {
|
||||
LLOG("Quit thread");
|
||||
return true;
|
||||
}
|
||||
//MJob job = jobs.Pop(); //already done above
|
||||
e->Unlink();
|
||||
--jobcount;
|
||||
if(e == lastjob)
|
||||
lastjob = list; //so we knoe where to insert next
|
||||
MJob & job = *e;
|
||||
lock.Leave();
|
||||
job.cb();
|
||||
lock.Enter();
|
||||
e->LinkBefore(list); //part of pop
|
||||
if(--job.work->todo <= 0) {
|
||||
LLOG("Releasing waitforfinish of (WorkQueue " << FormatIntHex(job.work) << ")");
|
||||
job.work->waitforfinish.Release(); //multiple call, but semaphore cant become negative anyway
|
||||
}
|
||||
LLOG("Finished, remaining todo " << job.work->todo << " (WorkQueue " << FormatIntHex(job.work) << ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
void WorkQueue::Pool::ThreadRun(int tno)
|
||||
{
|
||||
LLOG("WorkQueue thread #" << tno << " started");
|
||||
lock.Enter();
|
||||
for(;;) {
|
||||
// while(jobs.GetCount() <= 0) {
|
||||
while(jobcount <= 0) {
|
||||
waiting_threads++;
|
||||
lock.Leave();
|
||||
LLOG("#" << tno << " Waiting for job");
|
||||
waitforjob.Wait();
|
||||
LLOG("#" << tno << " Waiting ended");
|
||||
lock.Enter();
|
||||
}
|
||||
LLOG("#" << tno << " Job acquired");
|
||||
if(DoJob())
|
||||
break;
|
||||
LLOG("#" << tno << " Job finished");
|
||||
}
|
||||
lock.Leave();
|
||||
LLOG("WorkQueue thread #" << tno << " finished");
|
||||
}
|
||||
|
||||
WorkQueue::MJob & WorkQueue::Pool::NextFree()
|
||||
{
|
||||
MJob *list = _jobs.GetPtr();
|
||||
MJob *e = list->GetNext();
|
||||
if(e != list && e->work == NULL) //as long as not end
|
||||
{
|
||||
//found a free one
|
||||
e->Unlink();
|
||||
e->LinkAfter(lastjob); //list
|
||||
}
|
||||
else
|
||||
{
|
||||
//need one more, either end or used
|
||||
//e = list->InsertNext();
|
||||
e = lastjob->InsertNext();
|
||||
lastjob = e;
|
||||
}
|
||||
++jobcount;
|
||||
return *e;
|
||||
}
|
||||
|
||||
void WorkQueue::Do(Callback cb) {
|
||||
Pool& p = pool();
|
||||
p.lock.Enter();
|
||||
// if(p.jobs.GetCount() > 128) {
|
||||
if(p.jobcount > 128) {
|
||||
LLOG("Stack full: running in the main thread");
|
||||
p.lock.Leave();
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
//MJob job;
|
||||
MJob & job = p.NextFree();
|
||||
job.cb = cb;
|
||||
job.work = this;
|
||||
//p.jobs.Add(job); //already done
|
||||
todo++;
|
||||
LLOG("Adding job; todo: " << todo << " (WorkQueue " << FormatIntHex(this) << ")");
|
||||
if(p.waiting_threads>0) {
|
||||
LLOG("Releasing thread waiting for job: " << p.waiting_threads);
|
||||
p.waiting_threads--;
|
||||
p.waitforjob.Release();
|
||||
}
|
||||
p.lock.Leave();
|
||||
}
|
||||
|
||||
void WorkQueue::Finish() {
|
||||
#ifdef _MULTITHREADED
|
||||
Pool &p = pool();
|
||||
p.lock.Enter();
|
||||
while(todo>0) {
|
||||
LLOG("Finish: todo: " << todo << " (WorkQueue " << FormatIntHex(this) << ")");
|
||||
if(p.jobcount>0)
|
||||
p.DoJob();
|
||||
else {
|
||||
p.lock.Leave();
|
||||
LLOG("WaitForFinish (WorkQueue " << FormatIntHex(this) << ")");
|
||||
waitforfinish.Wait();
|
||||
p.lock.Enter();
|
||||
}
|
||||
}
|
||||
p.lock.Leave();
|
||||
LLOG("WorkQueue finished");
|
||||
#endif
|
||||
}
|
||||
|
||||
WorkQueue::WorkQueue()
|
||||
: _pool(1)
|
||||
{
|
||||
LLOG("*** WorkQueue constructed " << FormatHex(this));
|
||||
todo = 0;
|
||||
}
|
||||
|
||||
WorkQueue::~WorkQueue()
|
||||
{
|
||||
Finish();
|
||||
LLOG("~~~ WorkQueue destructed");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} //namespace
|
||||
77
bazaar/WorkQueue/WorkQueue.h
Normal file
77
bazaar/WorkQueue/WorkQueue.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#ifndef _WorkQueue_WorkQueue_h_
|
||||
#define _WorkQueue_WorkQueue_h_
|
||||
|
||||
#include <Core/Core.h>
|
||||
|
||||
using namespace Upp;
|
||||
|
||||
namespace MyWQ
|
||||
{
|
||||
|
||||
#ifdef _MULTITHREADED
|
||||
|
||||
class WorkQueue : NoCopy {
|
||||
|
||||
struct MJob
|
||||
: public Link<MJob>
|
||||
, Moveable<MJob>
|
||||
{
|
||||
Callback cb;
|
||||
WorkQueue *work;
|
||||
MJob() : work(NULL) {}
|
||||
};
|
||||
|
||||
struct Pool {
|
||||
typedef Pool CLASSNAME;
|
||||
//Vector<MJob> jobs;
|
||||
LinkOwner<MJob> _jobs;
|
||||
int jobcount;
|
||||
MJob & NextFree();
|
||||
Link<MJob> * lastjob;
|
||||
|
||||
int waiting_threads;
|
||||
Array<Thread> threads;
|
||||
|
||||
CriticalSection lock;
|
||||
Semaphore waitforjob;
|
||||
|
||||
Pool(int threadnr = -1);
|
||||
~Pool();
|
||||
|
||||
bool DoJob();
|
||||
void ThreadRun(int tno);
|
||||
};
|
||||
|
||||
friend struct Pool;
|
||||
|
||||
//static Pool& pool();
|
||||
Pool _pool;
|
||||
Pool& pool();
|
||||
|
||||
Semaphore waitforfinish;
|
||||
int todo;
|
||||
|
||||
public:
|
||||
void Do(Callback cb);
|
||||
WorkQueue& operator&(Callback cb) { Do(cb); return *this; }
|
||||
|
||||
void Finish();
|
||||
|
||||
WorkQueue();
|
||||
~WorkQueue();
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class WorkQueue : NoCopy {
|
||||
public:
|
||||
void Do(Callback cb) { cb(); }
|
||||
WorkQueue& operator&(Callback cb) { cb(); return *this; }
|
||||
void Finish() {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} //namespace
|
||||
|
||||
#endif
|
||||
9
bazaar/WorkQueue/WorkQueue.upp
Normal file
9
bazaar/WorkQueue/WorkQueue.upp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
description "a thread WorkQueue implementation for serializeing off main thread work\377";
|
||||
|
||||
uses
|
||||
Core;
|
||||
|
||||
file
|
||||
WorkQueue.h,
|
||||
WorkQueue.cpp;
|
||||
|
||||
45
bazaar/WorkQueueTest/WorkQueueTest.h
Normal file
45
bazaar/WorkQueueTest/WorkQueueTest.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef _WorkQueueTest_WorkQueueTest_h
|
||||
#define _WorkQueueTest_WorkQueueTest_h
|
||||
|
||||
#include <CtrlLib/CtrlLib.h>
|
||||
|
||||
using namespace Upp;
|
||||
|
||||
#define LAYOUTFILE <WorkQueueTest/WorkQueueTest.lay>
|
||||
#include <CtrlCore/lay.h>
|
||||
|
||||
#define IMAGEFILE <WorkQueueTest/WorkQueueTest.iml>
|
||||
#include <Draw/iml_header.h>
|
||||
|
||||
#include <CoWork/CoWork.h>
|
||||
|
||||
class WorkQueueTest : public WithWorkQueueTestLayout<TopWindow> {
|
||||
public:
|
||||
typedef WorkQueueTest CLASSNAME;
|
||||
WorkQueueTest();
|
||||
|
||||
void Work();
|
||||
virtual void Paint(Draw& w);
|
||||
virtual void LeftDown(Point p, dword keyflags);
|
||||
|
||||
virtual void Close()
|
||||
{
|
||||
//PostCallback(THISBACK(DoClose)); //results in deadlock, since PostCallback stuff is executed in GuiLock
|
||||
//so the the worqueue cant complete. it's trying to do
|
||||
//Refresh stuff in GuiLock environment, but cant,
|
||||
//since the Postcallback is executing, and waiting for the thread to complete
|
||||
//classical deadlock, mismatched order.. but is same with CoWork
|
||||
Thread::Start(THISBACK(DoClose)); //so schedule Finish Wait in GuiLock free env.
|
||||
}
|
||||
void DoClose()
|
||||
{
|
||||
wq.Finish();
|
||||
TopWindow::Close();
|
||||
}
|
||||
|
||||
MyCoWork::WorkQueue wq;
|
||||
int phase;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
7
bazaar/WorkQueueTest/WorkQueueTest.iml
Normal file
7
bazaar/WorkQueueTest/WorkQueueTest.iml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
PREMULTIPLIED
|
||||
IMAGE_ID(icon)
|
||||
|
||||
IMAGE_BEGIN_DATA
|
||||
IMAGE_DATA(120,156,99,16,96,16,96,96,2,66,16,248,175,197,240,127,48,96,139,85,164,225,81,253,163,250,135,147,254,129,194,0)
|
||||
IMAGE_DATA(130,184,0,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
|
||||
IMAGE_END_DATA(64, 1)
|
||||
4
bazaar/WorkQueueTest/WorkQueueTest.lay
Normal file
4
bazaar/WorkQueueTest/WorkQueueTest.lay
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
LAYOUT(WorkQueueTestLayout, 412, 100)
|
||||
ITEM(SliderCtrl, sl, LeftPosZ(0, 412).TopPosZ(76, 24))
|
||||
END_LAYOUT
|
||||
|
||||
15
bazaar/WorkQueueTest/WorkQueueTest.upp
Normal file
15
bazaar/WorkQueueTest/WorkQueueTest.upp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
description "WorkQueue derived from custom CoWork\377";
|
||||
|
||||
uses
|
||||
CtrlLib,
|
||||
CoWork;
|
||||
|
||||
file
|
||||
WorkQueueTest.h,
|
||||
main.cpp,
|
||||
WorkQueueTest.lay,
|
||||
WorkQueueTest.iml;
|
||||
|
||||
mainconfig
|
||||
"" = "GUI MT";
|
||||
|
||||
5
bazaar/WorkQueueTest/init
Normal file
5
bazaar/WorkQueueTest/init
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#ifndef _WorkQueueTest_icpp_init_stub
|
||||
#define _WorkQueueTest_icpp_init_stub
|
||||
#include "CtrlLib/init"
|
||||
#include "CoWork/init"
|
||||
#endif
|
||||
42
bazaar/WorkQueueTest/main.cpp
Normal file
42
bazaar/WorkQueueTest/main.cpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#include "WorkQueueTest.h"
|
||||
|
||||
#define IMAGEFILE <WorkQueueTest/WorkQueueTest.iml>
|
||||
#include <Draw/iml_source.h>
|
||||
|
||||
WorkQueueTest::WorkQueueTest()
|
||||
{
|
||||
CtrlLayout(*this, "Window title");
|
||||
phase = 0;
|
||||
sl.SetData(50);
|
||||
}
|
||||
|
||||
void WorkQueueTest::Work()
|
||||
{
|
||||
Sleep(1000); //some loooong work to process
|
||||
|
||||
//show results
|
||||
Size sz = GetSize();
|
||||
(++phase) %= sz.cx;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void WorkQueueTest::LeftDown(Point p, dword keyflags)
|
||||
{
|
||||
//do something, and offload / queue independant work to the workqueue, it will be processed in order
|
||||
//but in a worker thread, so main thread can react on user interaction (slider)
|
||||
wq & THISBACK(Work);
|
||||
}
|
||||
|
||||
void WorkQueueTest::Paint(Draw& w)
|
||||
{
|
||||
Size sz = GetSize();
|
||||
w.DrawRect(0,0,sz.cx,sz.cy, SColorFace());
|
||||
w.DrawText(0, 10, "Multiple Click left to queue work, use slider while processing");
|
||||
w.DrawImage(phase, sz.cy/2, IMAGECLASS::icon());
|
||||
}
|
||||
|
||||
GUI_APP_MAIN
|
||||
{
|
||||
WorkQueueTest().Run();
|
||||
}
|
||||
|
||||
44
bazaar/WorkQueueTest2/WorkQueueTest.h
Normal file
44
bazaar/WorkQueueTest2/WorkQueueTest.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef _WorkQueueTest_WorkQueueTest_h
|
||||
#define _WorkQueueTest_WorkQueueTest_h
|
||||
|
||||
#include <CtrlLib/CtrlLib.h>
|
||||
|
||||
using namespace Upp;
|
||||
|
||||
#define LAYOUTFILE <WorkQueueTest2/WorkQueueTest.lay>
|
||||
#include <CtrlCore/lay.h>
|
||||
|
||||
#define IMAGEFILE <WorkQueueTest2/WorkQueueTest.iml>
|
||||
#include <Draw/iml_header.h>
|
||||
|
||||
#include <WorkQueue/WorkQueue.h>
|
||||
|
||||
class WorkQueueTest : public WithWorkQueueTestLayout<TopWindow> {
|
||||
public:
|
||||
typedef WorkQueueTest CLASSNAME;
|
||||
WorkQueueTest();
|
||||
|
||||
void Work(int i);
|
||||
void WorkSelf();
|
||||
void WorkQ();
|
||||
|
||||
virtual void Close()
|
||||
{
|
||||
//PostCallback(THISBACK(DoClose)); //results in deadlock, since PostCallback stuff is executed in GuiLock
|
||||
//so the the worqueue cant complete. it's trying to do
|
||||
//Refresh stuff in GuiLock environment, but cant,
|
||||
//since the Postcallback is executing, and waiting for the thread to complete
|
||||
//classical deadlock, mismatched order.. but is same with CoWork
|
||||
Thread::Start(THISBACK(DoClose)); //so schedule Finish Wait in GuiLock free env.
|
||||
}
|
||||
void DoClose()
|
||||
{
|
||||
wq.Finish();
|
||||
TopWindow::Close();
|
||||
}
|
||||
|
||||
MyWQ::WorkQueue wq;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
8
bazaar/WorkQueueTest2/WorkQueueTest.iml
Normal file
8
bazaar/WorkQueueTest2/WorkQueueTest.iml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
PREMULTIPLIED
|
||||
IMAGE_ID(icon)
|
||||
|
||||
IMAGE_BEGIN_DATA
|
||||
IMAGE_DATA(120,156,99,16,96,16,96,128,129,255,90,12,255,7,3,6,57,133,129,4,64,162,126,12,121,50,245,195,213,144,233,126)
|
||||
IMAGE_DATA(74,245,195,205,192,162,31,197,124,42,233,71,119,215,127,2,250,209,205,192,166,159,129,128,126,92,102,16,10,127,108,238)
|
||||
IMAGE_DATA(198,25,174,4,244,19,4,212,214,63,80,24,0,145,220,17,144,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
|
||||
IMAGE_END_DATA(96, 1)
|
||||
7
bazaar/WorkQueueTest2/WorkQueueTest.lay
Normal file
7
bazaar/WorkQueueTest2/WorkQueueTest.lay
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
LAYOUT(WorkQueueTestLayout, 372, 320)
|
||||
ITEM(DocEdit, inf, HSizePosZ(4, 4).VSizePosZ(4, 24))
|
||||
ITEM(Button, workq, SetLabel(t_("WorkQ")).RightPosZ(0, 92).BottomPosZ(0, 20))
|
||||
ITEM(Button, self, SetLabel(t_("Self")).RightPosZ(92, 100).BottomPosZ(0, 20))
|
||||
ITEM(SliderCtrl, sl, LeftPosZ(4, 172).TopPosZ(296, 24))
|
||||
END_LAYOUT
|
||||
|
||||
15
bazaar/WorkQueueTest2/WorkQueueTest2.upp
Normal file
15
bazaar/WorkQueueTest2/WorkQueueTest2.upp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
description "own WorkQueue test package\377";
|
||||
|
||||
uses
|
||||
CtrlLib,
|
||||
WorkQueue;
|
||||
|
||||
file
|
||||
WorkQueueTest.h,
|
||||
main.cpp,
|
||||
WorkQueueTest.lay,
|
||||
WorkQueueTest.iml;
|
||||
|
||||
mainconfig
|
||||
"" = "GUI MT";
|
||||
|
||||
5
bazaar/WorkQueueTest2/init
Normal file
5
bazaar/WorkQueueTest2/init
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#ifndef _WorkQueueTest2_icpp_init_stub
|
||||
#define _WorkQueueTest2_icpp_init_stub
|
||||
#include "CtrlLib/init"
|
||||
#include "WorkQueue/init"
|
||||
#endif
|
||||
45
bazaar/WorkQueueTest2/main.cpp
Normal file
45
bazaar/WorkQueueTest2/main.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#include "WorkQueueTest.h"
|
||||
|
||||
#define IMAGEFILE <WorkQueueTest/WorkQueueTest.iml>
|
||||
#include <Draw/iml_source.h>
|
||||
|
||||
WorkQueueTest::WorkQueueTest()
|
||||
{
|
||||
CtrlLayout(*this, "Window title");
|
||||
|
||||
self <<= THISBACK(WorkSelf);
|
||||
workq <<= THISBACK(WorkQ);
|
||||
|
||||
inf.Insert(0, "Cant use slider when Work Self, but can use it when offloading to workqueue");
|
||||
sl.SetData(50);
|
||||
}
|
||||
|
||||
void WorkQueueTest::WorkSelf()
|
||||
{
|
||||
inf.Clear();
|
||||
for(int i = 0; i < 100; i++)
|
||||
Work(i);
|
||||
}
|
||||
|
||||
void WorkQueueTest::WorkQ()
|
||||
{
|
||||
inf.Clear();
|
||||
for(int i = 0; i < 100; i++)
|
||||
wq & THISBACK1(Work, i);
|
||||
}
|
||||
|
||||
void WorkQueueTest::Work(int i)
|
||||
{
|
||||
Sleep(Random()%200); //some loong work to process
|
||||
|
||||
//show results
|
||||
GuiLock __;
|
||||
inf.Insert(inf.GetCursor(), String().Cat() << "-|" << i << "|-");
|
||||
inf.SetCursor(inf.GetLength());
|
||||
}
|
||||
|
||||
GUI_APP_MAIN
|
||||
{
|
||||
WorkQueueTest().Run();
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue