From 00369e68756e8e39423a7d41d8ea1d10ca179081 Mon Sep 17 00:00:00 2001 From: kohait Date: Tue, 17 Aug 2010 21:10:05 +0000 Subject: [PATCH] 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 --- bazaar/CoWork/CoWork.cpp | 208 +++++++++++++++++++++++ bazaar/CoWork/CoWork.h | 85 +++++++++ bazaar/CoWork/CoWork.upp | 9 + bazaar/CoWork/Img.iml | 1 + bazaar/CoWork/init | 4 + bazaar/WorkQueue/WorkQueue.cpp | 178 +++++++++++++++++++ bazaar/WorkQueue/WorkQueue.h | 77 +++++++++ bazaar/WorkQueue/WorkQueue.upp | 9 + bazaar/WorkQueueTest/WorkQueueTest.h | 45 +++++ bazaar/WorkQueueTest/WorkQueueTest.iml | 7 + bazaar/WorkQueueTest/WorkQueueTest.lay | 4 + bazaar/WorkQueueTest/WorkQueueTest.upp | 15 ++ bazaar/WorkQueueTest/init | 5 + bazaar/WorkQueueTest/main.cpp | 42 +++++ bazaar/WorkQueueTest2/WorkQueueTest.h | 44 +++++ bazaar/WorkQueueTest2/WorkQueueTest.iml | 8 + bazaar/WorkQueueTest2/WorkQueueTest.lay | 7 + bazaar/WorkQueueTest2/WorkQueueTest2.upp | 15 ++ bazaar/WorkQueueTest2/init | 5 + bazaar/WorkQueueTest2/main.cpp | 45 +++++ 20 files changed, 813 insertions(+) create mode 100644 bazaar/CoWork/CoWork.cpp create mode 100644 bazaar/CoWork/CoWork.h create mode 100644 bazaar/CoWork/CoWork.upp create mode 100644 bazaar/CoWork/Img.iml create mode 100644 bazaar/CoWork/init create mode 100644 bazaar/WorkQueue/WorkQueue.cpp create mode 100644 bazaar/WorkQueue/WorkQueue.h create mode 100644 bazaar/WorkQueue/WorkQueue.upp create mode 100644 bazaar/WorkQueueTest/WorkQueueTest.h create mode 100644 bazaar/WorkQueueTest/WorkQueueTest.iml create mode 100644 bazaar/WorkQueueTest/WorkQueueTest.lay create mode 100644 bazaar/WorkQueueTest/WorkQueueTest.upp create mode 100644 bazaar/WorkQueueTest/init create mode 100644 bazaar/WorkQueueTest/main.cpp create mode 100644 bazaar/WorkQueueTest2/WorkQueueTest.h create mode 100644 bazaar/WorkQueueTest2/WorkQueueTest.iml create mode 100644 bazaar/WorkQueueTest2/WorkQueueTest.lay create mode 100644 bazaar/WorkQueueTest2/WorkQueueTest2.upp create mode 100644 bazaar/WorkQueueTest2/init create mode 100644 bazaar/WorkQueueTest2/main.cpp diff --git a/bazaar/CoWork/CoWork.cpp b/bazaar/CoWork/CoWork.cpp new file mode 100644 index 000000000..2eef49d11 --- /dev/null +++ b/bazaar/CoWork/CoWork.cpp @@ -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 diff --git a/bazaar/CoWork/CoWork.h b/bazaar/CoWork/CoWork.h new file mode 100644 index 000000000..337a0e62b --- /dev/null +++ b/bazaar/CoWork/CoWork.h @@ -0,0 +1,85 @@ +#ifndef _CoWork_CoWork_h +#define _CoWork_CoWork_h + +#include + +using namespace Upp; + +namespace MyCoWork +{ + +#ifdef _MULTITHREADED + +class CoWork : NoCopy { + + struct MJob : Moveable { + Callback cb; + CoWork *work; + }; + + struct Pool { + typedef Pool CLASSNAME; + Vector jobs; + int waiting_threads; + ArrayMap 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 _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 diff --git a/bazaar/CoWork/CoWork.upp b/bazaar/CoWork/CoWork.upp new file mode 100644 index 000000000..5d89e5cd4 --- /dev/null +++ b/bazaar/CoWork/CoWork.upp @@ -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; + diff --git a/bazaar/CoWork/Img.iml b/bazaar/CoWork/Img.iml new file mode 100644 index 000000000..a5b0ccf38 --- /dev/null +++ b/bazaar/CoWork/Img.iml @@ -0,0 +1 @@ +PREMULTIPLIED diff --git a/bazaar/CoWork/init b/bazaar/CoWork/init new file mode 100644 index 000000000..78d5974f4 --- /dev/null +++ b/bazaar/CoWork/init @@ -0,0 +1,4 @@ +#ifndef _CoWork_icpp_init_stub +#define _CoWork_icpp_init_stub +#include "Core/init" +#endif diff --git a/bazaar/WorkQueue/WorkQueue.cpp b/bazaar/WorkQueue/WorkQueue.cpp new file mode 100644 index 000000000..83d0fe2bf --- /dev/null +++ b/bazaar/WorkQueue/WorkQueue.cpp @@ -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 \ No newline at end of file diff --git a/bazaar/WorkQueue/WorkQueue.h b/bazaar/WorkQueue/WorkQueue.h new file mode 100644 index 000000000..66f202984 --- /dev/null +++ b/bazaar/WorkQueue/WorkQueue.h @@ -0,0 +1,77 @@ +#ifndef _WorkQueue_WorkQueue_h_ +#define _WorkQueue_WorkQueue_h_ + +#include + +using namespace Upp; + +namespace MyWQ +{ + +#ifdef _MULTITHREADED + +class WorkQueue : NoCopy { + + struct MJob + : public Link + , Moveable + { + Callback cb; + WorkQueue *work; + MJob() : work(NULL) {} + }; + + struct Pool { + typedef Pool CLASSNAME; + //Vector jobs; + LinkOwner _jobs; + int jobcount; + MJob & NextFree(); + Link * lastjob; + + int waiting_threads; + Array 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 diff --git a/bazaar/WorkQueue/WorkQueue.upp b/bazaar/WorkQueue/WorkQueue.upp new file mode 100644 index 000000000..9b8cefc2d --- /dev/null +++ b/bazaar/WorkQueue/WorkQueue.upp @@ -0,0 +1,9 @@ +description "a thread WorkQueue implementation for serializeing off main thread work\377"; + +uses + Core; + +file + WorkQueue.h, + WorkQueue.cpp; + diff --git a/bazaar/WorkQueueTest/WorkQueueTest.h b/bazaar/WorkQueueTest/WorkQueueTest.h new file mode 100644 index 000000000..1fc612ecd --- /dev/null +++ b/bazaar/WorkQueueTest/WorkQueueTest.h @@ -0,0 +1,45 @@ +#ifndef _WorkQueueTest_WorkQueueTest_h +#define _WorkQueueTest_WorkQueueTest_h + +#include + +using namespace Upp; + +#define LAYOUTFILE +#include + +#define IMAGEFILE +#include + +#include + +class WorkQueueTest : public WithWorkQueueTestLayout { +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 + diff --git a/bazaar/WorkQueueTest/WorkQueueTest.iml b/bazaar/WorkQueueTest/WorkQueueTest.iml new file mode 100644 index 000000000..1b0cf7a38 --- /dev/null +++ b/bazaar/WorkQueueTest/WorkQueueTest.iml @@ -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) diff --git a/bazaar/WorkQueueTest/WorkQueueTest.lay b/bazaar/WorkQueueTest/WorkQueueTest.lay new file mode 100644 index 000000000..80846f54a --- /dev/null +++ b/bazaar/WorkQueueTest/WorkQueueTest.lay @@ -0,0 +1,4 @@ +LAYOUT(WorkQueueTestLayout, 412, 100) + ITEM(SliderCtrl, sl, LeftPosZ(0, 412).TopPosZ(76, 24)) +END_LAYOUT + diff --git a/bazaar/WorkQueueTest/WorkQueueTest.upp b/bazaar/WorkQueueTest/WorkQueueTest.upp new file mode 100644 index 000000000..1d337b440 --- /dev/null +++ b/bazaar/WorkQueueTest/WorkQueueTest.upp @@ -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"; + diff --git a/bazaar/WorkQueueTest/init b/bazaar/WorkQueueTest/init new file mode 100644 index 000000000..fecec6441 --- /dev/null +++ b/bazaar/WorkQueueTest/init @@ -0,0 +1,5 @@ +#ifndef _WorkQueueTest_icpp_init_stub +#define _WorkQueueTest_icpp_init_stub +#include "CtrlLib/init" +#include "CoWork/init" +#endif diff --git a/bazaar/WorkQueueTest/main.cpp b/bazaar/WorkQueueTest/main.cpp new file mode 100644 index 000000000..10f6ef220 --- /dev/null +++ b/bazaar/WorkQueueTest/main.cpp @@ -0,0 +1,42 @@ +#include "WorkQueueTest.h" + +#define IMAGEFILE +#include + +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(); +} + diff --git a/bazaar/WorkQueueTest2/WorkQueueTest.h b/bazaar/WorkQueueTest2/WorkQueueTest.h new file mode 100644 index 000000000..7559f8beb --- /dev/null +++ b/bazaar/WorkQueueTest2/WorkQueueTest.h @@ -0,0 +1,44 @@ +#ifndef _WorkQueueTest_WorkQueueTest_h +#define _WorkQueueTest_WorkQueueTest_h + +#include + +using namespace Upp; + +#define LAYOUTFILE +#include + +#define IMAGEFILE +#include + +#include + +class WorkQueueTest : public WithWorkQueueTestLayout { +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 + diff --git a/bazaar/WorkQueueTest2/WorkQueueTest.iml b/bazaar/WorkQueueTest2/WorkQueueTest.iml new file mode 100644 index 000000000..977ef2973 --- /dev/null +++ b/bazaar/WorkQueueTest2/WorkQueueTest.iml @@ -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) diff --git a/bazaar/WorkQueueTest2/WorkQueueTest.lay b/bazaar/WorkQueueTest2/WorkQueueTest.lay new file mode 100644 index 000000000..84d8fc53c --- /dev/null +++ b/bazaar/WorkQueueTest2/WorkQueueTest.lay @@ -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 + diff --git a/bazaar/WorkQueueTest2/WorkQueueTest2.upp b/bazaar/WorkQueueTest2/WorkQueueTest2.upp new file mode 100644 index 000000000..bd6666071 --- /dev/null +++ b/bazaar/WorkQueueTest2/WorkQueueTest2.upp @@ -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"; + diff --git a/bazaar/WorkQueueTest2/init b/bazaar/WorkQueueTest2/init new file mode 100644 index 000000000..4b547b2f1 --- /dev/null +++ b/bazaar/WorkQueueTest2/init @@ -0,0 +1,5 @@ +#ifndef _WorkQueueTest2_icpp_init_stub +#define _WorkQueueTest2_icpp_init_stub +#include "CtrlLib/init" +#include "WorkQueue/init" +#endif diff --git a/bazaar/WorkQueueTest2/main.cpp b/bazaar/WorkQueueTest2/main.cpp new file mode 100644 index 000000000..b43c3853a --- /dev/null +++ b/bazaar/WorkQueueTest2/main.cpp @@ -0,0 +1,45 @@ +#include "WorkQueueTest.h" + +#define IMAGEFILE +#include + +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(); +} +