mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-07-11 22:03:01 -06:00
Altenative MultiThreading package, plus example added to Bazaar on behalf of Pavel Ostapenko (Mindtraveller)
git-svn-id: svn://ultimatepp.org/upp/trunk@1395 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
cdf8927206
commit
1276dbc3ff
9 changed files with 482 additions and 0 deletions
3
bazaar/MtAlt/MtAlt.cpp
Normal file
3
bazaar/MtAlt/MtAlt.cpp
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#include "MtAlt.h"
|
||||
|
||||
VectorMap<int, Vector<CallbackQueue *> > CallbackQueue::queues;
|
||||
352
bazaar/MtAlt/MtAlt.h
Normal file
352
bazaar/MtAlt/MtAlt.h
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
#ifndef _CORE_alt_MTalt_h_
|
||||
#define _CORE_alt_MTalt_h_
|
||||
|
||||
#ifndef flagMT
|
||||
#error Please add 'MT' to build flags
|
||||
#endif
|
||||
|
||||
#include <Core/Core.h>
|
||||
using namespace Upp;
|
||||
|
||||
dword rdtsc();
|
||||
|
||||
class CallbackQueue : private NoCopy
|
||||
{
|
||||
public:
|
||||
CallbackQueue(int _id = 0)
|
||||
:shuttingDown(false)
|
||||
,started(false)
|
||||
,id(_id)
|
||||
{
|
||||
Vector<CallbackQueue *> &q = queues.GetAdd(id);
|
||||
q << this;
|
||||
}
|
||||
|
||||
virtual ~CallbackQueue()
|
||||
{
|
||||
qMutex.Enter();
|
||||
queue.Clear();
|
||||
qMutex.Leave();
|
||||
|
||||
Vector<CallbackQueue *> &q = queues.Get(id);
|
||||
for (int i=0; i<q.GetCount(); ++i)
|
||||
{
|
||||
if (q[i] == this)
|
||||
{
|
||||
q.Remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define LOCK_ADD_UNLOCK \
|
||||
qMutex.Enter(); \
|
||||
queue.AddTail(e); \
|
||||
qMutex.Leave(); \
|
||||
qSemaphore.Release();
|
||||
|
||||
void ClearQueue()
|
||||
{
|
||||
qMutex.Enter();
|
||||
queue.Clear();
|
||||
qMutex.Leave();
|
||||
}
|
||||
|
||||
template<class OBJECT>
|
||||
void Request(void (OBJECT::*m)())
|
||||
{
|
||||
Element0<OBJECT> *e = new Element0<OBJECT>;
|
||||
e->caller = static_cast<OBJECT *>(this);
|
||||
e->m = m;
|
||||
LOCK_ADD_UNLOCK;
|
||||
}
|
||||
|
||||
template<class OBJECT, class P1>
|
||||
void Request(void (OBJECT::*m)(pick_ P1 &), pick_ P1 &p1)
|
||||
{
|
||||
Element1<OBJECT, P1> *e = new Element1<OBJECT, P1>;
|
||||
e->caller = static_cast<OBJECT *>(this);
|
||||
e->m = m;
|
||||
e->p1 = p1;
|
||||
LOCK_ADD_UNLOCK;
|
||||
}
|
||||
|
||||
template<class OBJECT, class P1>
|
||||
void Request(void (OBJECT::*m)(P1), P1 p1)
|
||||
{
|
||||
Element1c<OBJECT, P1> *e = new Element1c<OBJECT, P1>;
|
||||
e->caller = static_cast<OBJECT *>(this);
|
||||
e->m = m;
|
||||
e->p1 = p1;
|
||||
LOCK_ADD_UNLOCK;
|
||||
}
|
||||
|
||||
template<class OBJECT, class P1, class P2>
|
||||
void Request(void (OBJECT::*m)(pick_ P1 &,pick_ P2 &), pick_ P1 &p1, pick_ P2 &p2)
|
||||
{
|
||||
Element2<OBJECT, P1, P2> *e = new Element2<OBJECT, P1, P2>;
|
||||
e->caller = static_cast<OBJECT *>(this);
|
||||
e->m = m;
|
||||
e->p1 = p1;
|
||||
e->p2 = p2;
|
||||
LOCK_ADD_UNLOCK;
|
||||
}
|
||||
|
||||
template<class OBJECT, class P1, class P2>
|
||||
void Request(void (OBJECT::*m)(P1,P2), P1 p1, P2 p2)
|
||||
{
|
||||
Element2c<OBJECT, P1, P2> *e = new Element2c<OBJECT, P1, P2>;
|
||||
e->caller = static_cast<OBJECT *>(this);
|
||||
e->m = m;
|
||||
e->p1 = p1;
|
||||
e->p2 = p2;
|
||||
LOCK_ADD_UNLOCK;
|
||||
}
|
||||
|
||||
template<class OBJECT, class P1, class P2, class P3>
|
||||
void Request(void (OBJECT::*m)(pick_ P1 &, pick_ P2 &, pick_ P3 &), pick_ P1 &p1, pick_ P2 &p2, pick_ P3 &p3)
|
||||
{
|
||||
Element3<OBJECT, P1, P2, P3> *e = new Element3<OBJECT, P1, P2, P3>;
|
||||
e->caller = static_cast<OBJECT *>(this);
|
||||
e->m = m;
|
||||
e->p1 = p1;
|
||||
e->p2 = p2;
|
||||
e->p3 = p3;
|
||||
LOCK_ADD_UNLOCK;
|
||||
}
|
||||
|
||||
template<class OBJECT, class P1, class P2, class P3>
|
||||
void Request(void (OBJECT::*m)(P1,P2,P3), P1 p1, P2 p2, P3 p3)
|
||||
{
|
||||
Element3c<OBJECT, P1, P2, P3> *e = new Element3c<OBJECT, P1, P2, P3>;
|
||||
e->caller = static_cast<OBJECT *>(this);
|
||||
e->m = m;
|
||||
e->p1 = p1;
|
||||
e->p2 = p2;
|
||||
e->p3 = p3;
|
||||
LOCK_ADD_UNLOCK;
|
||||
}
|
||||
|
||||
template<class OBJECT, class P1, class P2, class P3, class P4>
|
||||
void Request(void (OBJECT::*m)(pick_ P1 &, pick_ P2 &, pick_ P3 &, pick_ P4 &), pick_ P1 &p1, pick_ P2 &p2, pick_ P3 &p3, pick_ P4 &p4)
|
||||
{
|
||||
Element4<OBJECT, P1, P2, P3, P4> *e = new Element4<OBJECT, P1, P2, P3, P4>;
|
||||
e->caller = static_cast<OBJECT *>(this);
|
||||
e->m = m;
|
||||
e->p1 = p1;
|
||||
e->p2 = p2;
|
||||
e->p3 = p3;
|
||||
e->p4 = p4;
|
||||
LOCK_ADD_UNLOCK;
|
||||
}
|
||||
|
||||
template<class OBJECT, class P1, class P2, class P3, class P4>
|
||||
void Request(void (OBJECT::*m)(P1,P2,P3,P4), P1 p1, P2 p2, P3 p3, P4 p4)
|
||||
{
|
||||
Element4c<OBJECT, P1, P2, P3, P4> *e = new Element4c<OBJECT, P1, P2, P3, P4>;
|
||||
e->caller = static_cast<OBJECT *>(this);
|
||||
e->m = m;
|
||||
e->p1 = p1;
|
||||
e->p2 = p2;
|
||||
e->p3 = p3;
|
||||
e->p4 = p4;
|
||||
LOCK_ADD_UNLOCK;
|
||||
}
|
||||
|
||||
void DoTasks()
|
||||
{
|
||||
while (!IsShutdown())
|
||||
if (!Execute())
|
||||
return;
|
||||
}
|
||||
|
||||
void WaitDoTasksInf()
|
||||
{
|
||||
while (!IsShutdown())
|
||||
{
|
||||
qSemaphore.Wait();
|
||||
Execute();
|
||||
}
|
||||
}
|
||||
|
||||
int GetTasksCount() {qMutex.Enter(); int out=queue.GetCount(); qMutex.Leave(); return out;}
|
||||
|
||||
bool IsShutdown() {return shuttingDown;}
|
||||
bool IsStarted() {return started;}
|
||||
|
||||
static void StartAll(int id = 0)
|
||||
{
|
||||
Vector<CallbackQueue *> &q = queues.GetAdd(id);
|
||||
for (int i=0; i<q.GetCount(); ++i)
|
||||
q[i]->Start();
|
||||
}
|
||||
|
||||
static void ShutdownAll(int id = 0)
|
||||
{
|
||||
Vector<CallbackQueue *> &q = queues.GetAdd(id);
|
||||
for (int i=0; i<q.GetCount(); ++i)
|
||||
q[i]->Shutdown();
|
||||
}
|
||||
|
||||
virtual void Start() {started = true;}
|
||||
virtual void Shutdown()
|
||||
{
|
||||
if (!IsStarted() || IsShutdown())
|
||||
return;
|
||||
shuttingDown = true;
|
||||
Request(&CallbackQueue::DoNothingJustAwake);
|
||||
}
|
||||
|
||||
private:
|
||||
static VectorMap<int, Vector<CallbackQueue *> > queues;
|
||||
|
||||
bool Execute()
|
||||
{
|
||||
qMutex.Enter();
|
||||
if (queue.IsEmpty())
|
||||
{
|
||||
qMutex.Leave();
|
||||
return false;
|
||||
}
|
||||
|
||||
Element *e = queue.DetachHead();
|
||||
qMutex.Leave();
|
||||
e->Execute();
|
||||
delete e;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct Element
|
||||
{
|
||||
virtual ~Element() {}
|
||||
virtual void Execute() = 0;
|
||||
void *caller;
|
||||
};
|
||||
|
||||
template<class OBJECT>
|
||||
struct Element0 : public Element
|
||||
{
|
||||
virtual ~Element0() {}
|
||||
virtual void Execute() {((static_cast<OBJECT *>(caller))->*m)();}
|
||||
void (OBJECT::*m)();
|
||||
};
|
||||
|
||||
template<class OBJECT, class P1>
|
||||
struct Element1 : public Element
|
||||
{
|
||||
virtual ~Element1() {}
|
||||
virtual void Execute() {((static_cast<OBJECT *>(caller))->*m)(p1);}
|
||||
void (OBJECT::*m)(pick_ P1 &p1);
|
||||
P1 p1;
|
||||
};
|
||||
|
||||
template<class OBJECT, class P1>
|
||||
struct Element1c : public Element
|
||||
{
|
||||
virtual ~Element1c() {}
|
||||
virtual void Execute() {((static_cast<OBJECT *>(caller))->*m)(p1);}
|
||||
void (OBJECT::*m)(P1 p1);
|
||||
P1 p1;
|
||||
};
|
||||
|
||||
template<class OBJECT, class P1, class P2>
|
||||
struct Element2 : public Element
|
||||
{
|
||||
virtual ~Element2() {}
|
||||
virtual void Execute() {((static_cast<OBJECT *>(caller))->*m)(p1,p2);}
|
||||
void (OBJECT::*m)(pick_ P1 &p1, pick_ P2 &p2);
|
||||
P1 p1;
|
||||
P2 p2;
|
||||
};
|
||||
|
||||
template<class OBJECT, class P1, class P2>
|
||||
struct Element2c : public Element
|
||||
{
|
||||
virtual ~Element2c() {}
|
||||
virtual void Execute() {((static_cast<OBJECT *>(caller))->*m)(p1,p2);}
|
||||
void (OBJECT::*m)(P1 p1, P2 p2);
|
||||
P1 p1;
|
||||
P2 p2;
|
||||
};
|
||||
|
||||
template<class OBJECT, class P1, class P2, class P3>
|
||||
struct Element3 : public Element
|
||||
{
|
||||
virtual ~Element3() {}
|
||||
virtual void Execute() {((static_cast<OBJECT *>(caller))->*m)(p1,p2,p3);}
|
||||
void (OBJECT::*m)(pick_ P1 &p1, pick_ P2 &p2, pick_ P3 &p3);
|
||||
P1 p1;
|
||||
P2 p2;
|
||||
P3 p3;
|
||||
};
|
||||
|
||||
template<class OBJECT, class P1, class P2, class P3>
|
||||
struct Element3c : public Element
|
||||
{
|
||||
virtual ~Element3c() {}
|
||||
virtual void Execute() {((static_cast<OBJECT *>(caller))->*m)(p1,p2,p3);}
|
||||
void (OBJECT::*m)(P1 p1, P2 p2, P3 p3);
|
||||
P1 p1;
|
||||
P2 p2;
|
||||
P3 p3;
|
||||
};
|
||||
|
||||
template<class OBJECT, class P1, class P2, class P3, class P4>
|
||||
struct Element4 : public Element
|
||||
{
|
||||
virtual ~Element4() {}
|
||||
virtual void Execute() {((static_cast<OBJECT *>(caller))->*m)(p1,p2,p3,p4);}
|
||||
void (OBJECT::*m)(pick_ P1 &p1, pick_ P2 &p2, pick_ P3 &p3, pick_ P4 &p4);
|
||||
P1 p1;
|
||||
P2 p2;
|
||||
P3 p3;
|
||||
P4 p4;
|
||||
};
|
||||
|
||||
template<class OBJECT, class P1, class P2, class P3, class P4>
|
||||
struct Element4c : public Element
|
||||
{
|
||||
virtual ~Element4c() {}
|
||||
virtual void Execute() {((static_cast<OBJECT *>(caller))->*m)(p1,p2,p3,p4);}
|
||||
void (OBJECT::*m)(P1 p1, P2 p2, P3 p3, P4 p4);
|
||||
P1 p1;
|
||||
P2 p2;
|
||||
P3 p3;
|
||||
P4 p4;
|
||||
};
|
||||
|
||||
void DoNothingJustAwake(){}
|
||||
|
||||
BiArray<Element> queue;
|
||||
Mutex qMutex;
|
||||
Semaphore qSemaphore;
|
||||
bool shuttingDown;
|
||||
bool started;
|
||||
int id;
|
||||
};
|
||||
|
||||
class CallbackThread : public CallbackQueue, protected Thread
|
||||
{
|
||||
public:
|
||||
CallbackThread(int id = 0) :CallbackQueue(id) {}
|
||||
virtual ~CallbackThread() {Shutdown();}
|
||||
|
||||
virtual void Start()
|
||||
{
|
||||
CallbackQueue::Start();
|
||||
Thread::Run(callback(this, &CallbackThread::Run));
|
||||
}
|
||||
|
||||
virtual void Shutdown()
|
||||
{
|
||||
if (!IsStarted() || IsShutdown())
|
||||
return;
|
||||
CallbackQueue::Shutdown();
|
||||
Wait();
|
||||
}
|
||||
|
||||
private:
|
||||
void Run() {WaitDoTasksInf();}
|
||||
};
|
||||
|
||||
#endif
|
||||
12
bazaar/MtAlt/MtAlt.upp
Normal file
12
bazaar/MtAlt/MtAlt.upp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
description "Alternative multithreading by Pavel Ostapenko\377";
|
||||
|
||||
uses
|
||||
Core;
|
||||
|
||||
file
|
||||
MtAlt.cpp,
|
||||
MtAlt.h;
|
||||
|
||||
mainconfig
|
||||
"" = "MT";
|
||||
|
||||
4
bazaar/MtAlt/init
Normal file
4
bazaar/MtAlt/init
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#ifndef _MtAlt_icpp_init_stub
|
||||
#define _MtAlt_icpp_init_stub
|
||||
#include "Core/init"
|
||||
#endif
|
||||
54
bazaar/MtAltExample1/Main.cpp
Normal file
54
bazaar/MtAltExample1/Main.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#include <CtrlLib/CtrlLib.h>
|
||||
#include <MtAlt/MtAlt.h>
|
||||
#include "Worker.h"
|
||||
using namespace Upp;
|
||||
|
||||
#define LAYOUTFILE <MtAltExample1/MainWindow.lay>
|
||||
#include <CtrlCore/lay.h>
|
||||
|
||||
//thread GUI (main application thread)
|
||||
class MainWindow : public WithMainWindowLayout<TopWindow>, public CallbackQueue, public Worker::Notify
|
||||
{
|
||||
public: //thread GUI
|
||||
MainWindow();
|
||||
|
||||
public: //thread Worker
|
||||
virtual void OnWork2Result(int x1, int x2, String s) {Request(&MainWindow::Work2Result,x1,x2,s);}
|
||||
|
||||
private:
|
||||
void Work1() {worker.Request(&Worker::DoWork1);}
|
||||
|
||||
void Work2() {worker.Request(&Worker::DoWork2);}
|
||||
void Work2Result(int x1, int x2, String s)
|
||||
{
|
||||
log.Set(log.Get()+Format("Worker::Work2 %d, %d, %s\n", x1,x2,s));
|
||||
}
|
||||
|
||||
void DoTasksAndStatistics();
|
||||
|
||||
Worker worker;
|
||||
};
|
||||
|
||||
|
||||
MainWindow::MainWindow()
|
||||
:worker(this)
|
||||
{
|
||||
CtrlLayout(*this);
|
||||
button1 <<= callback(this, &MainWindow::Work1);
|
||||
button2 <<= callback(this, &MainWindow::Work2);
|
||||
|
||||
SetTimeCallback(-200, callback(this, &MainWindow::DoTasksAndStatistics));
|
||||
worker.Start();
|
||||
}
|
||||
|
||||
void MainWindow::DoTasksAndStatistics()
|
||||
{
|
||||
info.SetLabel(Format(" GUI: %d Worker: %d", GetTasksCount(), worker.GetTasksCount()));
|
||||
DoTasks();
|
||||
}
|
||||
|
||||
GUI_APP_MAIN
|
||||
{
|
||||
MainWindow().Sizeable().Run();
|
||||
}
|
||||
|
||||
7
bazaar/MtAltExample1/MainWindow.lay
Normal file
7
bazaar/MtAltExample1/MainWindow.lay
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
LAYOUT(MainWindowLayout, 504, 200)
|
||||
ITEM(DocEdit, log, HSizePosZ(12, 188).VSizePosZ(12, 12))
|
||||
ITEM(Button, button2, SetLabel(t_("Worker::Work2")).RightPosZ(12, 164).TopPosZ(44, 28))
|
||||
ITEM(Button, button1, SetLabel(t_("Worker::Work1")).RightPosZ(12, 164).TopPosZ(12, 28))
|
||||
ITEM(Label, info, SetFrame(BlackFrame()).RightPosZ(12, 164).TopPosZ(168, 19))
|
||||
END_LAYOUT
|
||||
|
||||
14
bazaar/MtAltExample1/MtAltExample1.upp
Normal file
14
bazaar/MtAltExample1/MtAltExample1.upp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
description "Example of alternative multithreading by Pavel Ostapenko\377B128,0,0";
|
||||
|
||||
uses
|
||||
CtrlLib,
|
||||
MtAlt;
|
||||
|
||||
file
|
||||
Main.cpp,
|
||||
Worker.h,
|
||||
MainWindow.lay;
|
||||
|
||||
mainconfig
|
||||
"" = "GUI MT";
|
||||
|
||||
31
bazaar/MtAltExample1/Worker.h
Normal file
31
bazaar/MtAltExample1/Worker.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef _MtAltExample1_OtherThreadClasses_h_
|
||||
#define _MtAltExample1_OtherThreadClasses_h_
|
||||
|
||||
#include <MtAlt/MtAlt.h>
|
||||
using namespace Upp;
|
||||
|
||||
//new thread 1
|
||||
class Worker : public CallbackThread
|
||||
{
|
||||
public: //thread GUI
|
||||
class Notify { public: virtual void OnWork2Result(int x1, int x2, String s) = 0; };
|
||||
|
||||
Worker(Notify *n) :notify(n) {}
|
||||
|
||||
public: //thread 1
|
||||
void DoWork1()
|
||||
{
|
||||
Sleep(300); //do something
|
||||
}
|
||||
void DoWork2()
|
||||
{
|
||||
Sleep(400); //do something
|
||||
Time t = GetSysTime();
|
||||
notify->OnWork2Result((int) Random(10), (int) Random(20), Format(t)); //notify result
|
||||
}
|
||||
|
||||
private:
|
||||
Notify *notify;
|
||||
};
|
||||
|
||||
#endif
|
||||
5
bazaar/MtAltExample1/init
Normal file
5
bazaar/MtAltExample1/init
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#ifndef _MtAltExample1_icpp_init_stub
|
||||
#define _MtAltExample1_icpp_init_stub
|
||||
#include "CtrlLib/init"
|
||||
#include "MtAlt/init"
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue