Why this code snippet stall?
The program intends to output firstsecondthird whereas the program stalls after firstsecond has been printed.
#include <condition_variable>
#include <mutex>
#include <thread>
#include <functional>
#include <iostream>
#include <vector>
class Foo {
public:
Foo() {
}
void first(std::function<void()> printFirst)
{
{
std::unique_lock<std::mutex> lk(mutex);
cv1.wait(lk, [this](){return 1==state;});
doing = 1;
// printFirst() outputs "first". Do not change or remove this line.
printFirst();
state = 2;
}
cv2.notify_one();
}
void second(std::function<void()> printSecond)
{
{
std::unique_lock<std::mutex> lk(mutex);
if(state !=2 )
{
if((1 == state)&&(1 != doing))
{
lk.unlock();
cv1.notify_one();
}
}
cv2.wait(lk, [this](){return 2==state;});
doing = 2;
// printSecond() outputs "second". Do not change or remove this line.
printSecond();
state = 3;
}
cv3.notify_one();
}
void third(std::function<void()> printThird)
{
{
std::unique_lock<std::mutex> lk(mutex);
if(state !=3 )
{
if((1 == state)&&(1 != doing))
{
lk.unlock();
cv1.notify_one();
}
else if((2 == state)&&(2 != doing))
{
lk.unlock();
cv2.notify_one();
}
}
cv3.wait(lk, [this](){return 3==state;});
// printThird() outputs "third". Do not change or remove this line.
printThird();
state = 3;
}
}
private:
std::condition_variable cv1;
std::condition_variable cv2;
std::condition_variable cv3;
std::mutex mutex;
int state{1};
int doing{0};
};
int main()
{
Foo foo;
std::vector<std::thread> threads;
std::this_thread::sleep_for(std::chrono::milliseconds(300));
threads.push_back(std::thread([&](){foo.second([]()->void{std::cout <<"second" <<std::endl;});}));
std::this_thread::sleep_for(std::chrono::milliseconds(300));
threads.push_back(std::thread([&](){foo.first([]()->void{std::cout <<"first" <<std::endl;});}));
std::this_thread::sleep_for(std::chrono::milliseconds(300));
threads.push_back(std::thread([&](){foo.third([]()->void{std::cout <<"third" <<std::endl;});}));
std::this_thread::sleep_for(std::chrono::seconds(2));
for(auto itr=threads.begin(); itr!=threads.end(); itr++)
{
itr->join();
}
}
Quoted from the comments of #Igor Tandetnik.
As per the document, which says that
template void wait (unique_lock& lck, Predicate pred);
lck
A unique_lock object whose mutex object is currently locked by
this thread. All concurrent calls to wait member functions of this
object shall use the same underlying mutex object (as returned by
lck.mutex()).
So, cv2.wait(lk, ...) requires that lk actually hold the mutex.
And if lk.unlock(); is removed, this code snippet could work as expected.
#include <condition_variable>
#include <mutex>
#include <thread>
#include <functional>
#include <iostream>
#include <vector>
class Foo {
public:
Foo() {
}
void first(std::function<void()> printFirst)
{
{
std::unique_lock<std::mutex> lk(mutex);
cv1.wait(lk, [this](){return 1==state;});
doing = 1;
// printFirst() outputs "first". Do not change or remove this line.
printFirst();
state = 2;
}
cv2.notify_one();
}
void second(std::function<void()> printSecond)
{
{
std::unique_lock<std::mutex> lk(mutex);
if(state !=2 )
{
if((1 == state)&&(1 != doing))
{
//lk.unlock(); //removed
cv1.notify_one();
}
}
cv2.wait(lk, [this](){return 2==state;});
doing = 2;
// printSecond() outputs "second". Do not change or remove this line.
printSecond();
state = 3;
}
cv3.notify_one();
}
void third(std::function<void()> printThird)
{
{
std::unique_lock<std::mutex> lk(mutex);
if(state !=3 )
{
if((1 == state)&&(1 != doing))
{
//lk.unlock(); //removed
cv1.notify_one();
}
else if((2 == state)&&(2 != doing))
{
//lk.unlock(); //removed
cv2.notify_one();
}
}
cv3.wait(lk, [this](){return 3==state;});
// printThird() outputs "third". Do not change or remove this line.
printThird();
state = 3;
}
}
private:
std::condition_variable cv1;
std::condition_variable cv2;
std::condition_variable cv3;
std::mutex mutex;
int state{1};
int doing{0};
};
int main()
{
Foo foo;
std::vector<std::thread> threads;
std::this_thread::sleep_for(std::chrono::milliseconds(300));
threads.push_back(std::thread([&](){foo.second([]()->void{std::cout <<"second" <<std::endl;});}));
std::this_thread::sleep_for(std::chrono::milliseconds(300));
threads.push_back(std::thread([&](){foo.first([]()->void{std::cout <<"first" <<std::endl;});}));
std::this_thread::sleep_for(std::chrono::milliseconds(300));
threads.push_back(std::thread([&](){foo.third([]()->void{std::cout <<"third" <<std::endl;});}));
std::this_thread::sleep_for(std::chrono::seconds(2));
for(auto itr=threads.begin(); itr!=threads.end(); itr++)
{
itr->join();
}
}
And the code snippet could be improved as this one:
#include <condition_variable>
#include <mutex>
#include <thread>
#include <functional>
#include <iostream>
#include <vector>
// #lc code=start
class Foo {
public:
Foo() {
}
void first(std::function<void()> printFirst)
{
{
std::unique_lock<std::mutex> lk(mutex);
// printFirst() outputs "first". Do not change or remove this line.
printFirst();
state = 2;
}
cv2.notify_one();
}
void second(std::function<void()> printSecond)
{
{
std::unique_lock<std::mutex> lk(mutex);
cv2.wait(lk, [this](){return 2==state;});
// printSecond() outputs "second". Do not change or remove this line.
printSecond();
state = 3;
}
cv3.notify_one();
}
void third(std::function<void()> printThird)
{
{
std::unique_lock<std::mutex> lk(mutex);
cv3.wait(lk, [this](){return 3==state;});
// printThird() outputs "third". Do not change or remove this line.
printThird();
state = 3;
}
}
private:
std::condition_variable cv2;
std::condition_variable cv3;
std::mutex mutex;
int state{1};
};
// #lc code=end
int main()
{
Foo foo;
std::vector<std::thread> threads;
std::this_thread::sleep_for(std::chrono::milliseconds(300));
threads.push_back(std::thread([&](){foo.second([]()->void{std::cout <<"second" <<std::endl;});}));
std::this_thread::sleep_for(std::chrono::milliseconds(300));
threads.push_back(std::thread([&](){foo.first([]()->void{std::cout <<"first" <<std::endl;});}));
std::this_thread::sleep_for(std::chrono::milliseconds(300));
threads.push_back(std::thread([&](){foo.third([]()->void{std::cout <<"third" <<std::endl;});}));
std::this_thread::sleep_for(std::chrono::seconds(2));
for(auto itr=threads.begin(); itr!=threads.end(); itr++)
{
itr->join();
}
}
Related
I've read that engines skip 1 or 2 frames and keep this distance to ensure that the render thread and the main thread won't go too much forward.
I've got a very simple command queue that allows the main thread to queue commands and the render thread to dispatch them, but I don't know how I can keep 1/2 frames distance between these threads.
basic implementation:
#include <iostream>
#include <queue>
#include <mutex>
#include <functional>
#include <thread>
#include <condition_variable>
struct CommandQueue
{
//not thread-safe
//called only by the main thread
//collects all gl calls from the main thread
void Submit(std::function<void()> command)
{
commands.push(std::move(command));
}
//called only by the main thread
//when the current frame has finished pushing gl commands
//we're ready to push them into the render thread
void Flush()
{
{
std::unique_lock<std::mutex> lock(mutex);
commandsToExecute = std::move(commands);
}
cv.notify_one();
}
//called only by the render thread
//submit gl calls from our queue into the graphics queue
bool Execute()
{
auto renderCommands = WaitForCommands();
if(renderCommands.empty()) {
return false;
}
while(!renderCommands.empty()) {
auto cmd = std::move(renderCommands.front());
renderCommands.pop();
cmd();
}
return true;
}
void Quit()
{
quit.store(true, std::memory_order_relaxed);
cv.notify_one();
}
private:
std::queue<std::function<void()>> WaitForCommands()
{
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [this]() { return !commandsToExecute.empty() || quit.load(std::memory_order_relaxed); });
auto result = std::move(commandsToExecute);
return result;
}
std::mutex mutex;
std::condition_variable cv;
std::queue<std::function<void()>> commands;
std::queue<std::function<void()>> commandsToExecute;
std::atomic_bool quit{false};
};
int main()
{
CommandQueue commandQueue;
std::thread renderThread([&](){
while(true) {
if(!commandQueue.Execute()) {
break;
}
}
});
bool quit = false;
while(!quit) {
//example commands...
commandQueue.Submit([](){
glClear(GL_COLOR_BUFFER_BIT);
});
commandQueue.Submit([](){
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
});
commandQueue.Submit([](){
glViewport(0, 0, 1600, 900);
});
commandQueue.Submit([](){
SDL_GL_SwapWindow(window);
});
//notify the render thread that there is work to be done
commandQueue.Flush();
}
commandQueue.Flush();
commandQueue.Quit();
renderThread.join();
return 0;
}
How can I implement this 1/2 frames lag?
I've found Filament Engine and it has FrameSkipper class which implements the solution I need.
quick example:
//what class should implement Tick functionality?
std::vector<std::function<bool()>> tickFunctions;
void RunAndRemove()
{
auto it = tickFunctions.begin();
while (it != tickFunctions.end()) {
if ((*it)()) {
it = tickFunctions.erase(it);
}
else ++it;
}
}
void Tick(CommandQueue& queue)
{
queue.Submit([]() { RunAndRemove(); });
}
struct Fence
{
Fence(CommandQueue& queue)
: queue(queue)
{
queue.Submit([this]() {
fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
std::weak_ptr<Status> weak = result;
tickFunctions.push_back([this, weak]() {
auto result = weak.lock();
if (result) {
GLenum status = glClientWaitSync(fence, 0, 0u);
result->store(status, std::memory_order_relaxed);
return (status != GL_TIMEOUT_EXPIRED);
}
return true;
});
});
}
~Fence()
{
queue.Submit([fence = fence]() {
glDeleteSync(fence);
});
}
GLenum getFenceStatus()
{
if (!result) {
return GL_TIMEOUT_EXPIRED;
}
return result->load();
}
GLsync fence = nullptr;
using Status = std::atomic<GLenum>;
std::shared_ptr<Status> result{ std::make_shared<Status>(GL_TIMEOUT_EXPIRED) };
CommandQueue& queue;
};
struct FrameSkipper
{
FrameSkipper(CommandQueue& queue, size_t latency = 1)
: queue(queue), last(latency)
{
}
~FrameSkipper() = default;
bool Begin()
{
auto& syncs = delayedSyncs;
auto sync = syncs.front();
if (sync) {
auto status = sync->getFenceStatus();
if (status == GL_TIMEOUT_EXPIRED) {
// Sync not ready, skip frame
return false;
}
sync.reset();
}
// shift all fences down by 1
std::move(syncs.begin() + 1, syncs.end(), syncs.begin());
syncs.back() = {};
return true;
}
void End()
{
auto& sync = delayedSyncs[last];
if (sync) {
sync.reset();
} sync = std::make_shared<Fence>(queue);
}
private:
static constexpr size_t MAX_FRAME_LATENCY = 4;
using Container = std::array<std::shared_ptr<Fence>, MAX_FRAME_LATENCY>;
mutable Container delayedSyncs{};
CommandQueue& queue;
size_t last;
};
int main()
{
CommandQueue commandQueue;
std::thread renderThread([&](){
while(true) {
if(!commandQueue.Execute()) {
break;
}
}
});
FrameSkipper frameSkipper(commandQueue);
auto BeginFrame = [&]() {
Tick(commandQueue);
if(frameSkipper.Begin()) {
return true;
}
commandQueue.Flush();
return false;
};
auto EndFrame = [&]() {
frameSkipper.End();
Tick(commandQueue);
commandQueue.Flush();
};
bool quit = false;
while(!quit) {
if(BeginFrame()) {
commandQueue.Submit([](){
glClear(GL_COLOR_BUFFER_BIT);
});
commandQueue.Submit([](){
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
});
commandQueue.Submit([](){
glViewport(0, 0, 1600, 900);
});
commandQueue.Submit([](){
SDL_GL_SwapWindow(window);
});
EndFrame();
}
}
commandQueue.Flush();
commandQueue.Quit();
renderThread.join();
return 0;
}
I am currently learning multithreading and semaphores and have been assigned to recreate the problem using only pthread. I found a solution that uses std::thread and have been working to convert it to pthreads, but I am having problems with the pthread_create method.
I am not sure specifically how to turn this statement
pthread_create(&threads[i], NULL, &Producer::run, &p);
into something that works with pthreads.
Here is my whole code for reference
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <deque>
#include <condition_variable>
#include <semaphore.h>
#include <queue>
#ifdef _WIN32
#include <windows.h>
void sleeps(unsigned milliseconds)
{
Sleep(milliseconds);
}
#else
#include <unistd.h>
void sleeps(unsigned milliseconds) {
usleep(milliseconds * 1000); // takes microseconds
}
#endif
class Widget {
public:
int data;
void setData(int data) {
this->data = data;
}
};
class Buffer
{
public:
void add(Widget widget) {
while (true) {
pthread_mutex_lock(&lock);
sharedBuffer.push_back(widget);
pthread_mutex_unlock(&lock);
return;
}
}
Widget remove() {
while(true) {
pthread_mutex_lock(&lock);
Widget backElem = sharedBuffer.back();
sharedBuffer.pop_back();
pthread_mutex_unlock(&lock);
return backElem;
}
}
Buffer() {}
private:
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
std::deque<Widget> sharedBuffer;
};
class Producer{
Buffer& sharedBuffer;
pthread_mutex_t coutMut;
public:
Producer(Buffer& buffer)
: sharedBuffer(buffer), coutMut(PTHREAD_MUTEX_INITIALIZER)
{}
void run() {
while(true) {
Widget widget;
widget.setData(rand() % 10);
sharedBuffer.add(widget);
pthread_mutex_lock(&coutMut);
std::cout << "Added: " << widget.data << "\n";
sleeps(50);
pthread_mutex_unlock(&coutMut);
}
}
};
class Consumer
{
Buffer& sharedBuffer;
pthread_mutex_t coutMut;
public:
Consumer(Buffer& buffer)
: sharedBuffer(buffer), coutMut(PTHREAD_MUTEX_INITIALIZER)
{}
void run() {
while(true) {
Widget widget;
widget = sharedBuffer.remove();
pthread_mutex_lock(&coutMut);
std::cout << "Removed: " << widget.data << "\n";
sleeps(50);
pthread_mutex_unlock(&coutMut);
}
}
};
int main(int argc, char *argv[]) {
typedef std::string string_std;
const int producerT = std::stoi(argv[1]);
const int consumerT = std::stoi(argv[2]);
int threadSize = producerT + consumerT;
pthread_t threads[threadSize];
void *status;
Buffer b1;
Producer p(b1);
Consumer c(b1);
for (int i = 0; i < producerT; i++) {
pthread_create(&threads[i], NULL, &Producer::run, &p);
}
for (int i = producerT; i < threadSize; i++) {
pthread_create(&threads[i], NULL, &Consumer::run, &c);
}
sleeps(5000);
for (int i = 0; i < threadSize; i++) {
pthread_join(threads[i], &status);
}
exit(0);
}
You are probably looking for something like this:
class Producer {
static void static_run(void* pThis) {
static_cast<Producer*>(pThis)->run();
}
void run() {
// Real work done here.
}
};
// At call site
Producer p;
pthread_create(&threads[i], NULL, Producer::static_run, &p);
The point is that pthread_create wants a C-style function - either a standalone non-member function, or a static member function. So you need an adapter (sometimes referred to as a "trampoline") that satisfies the requirements, and then turns around and calls the member function.
Main thread uses group of threads to search. First solution (test1) is often creating , waiting - join() and destroying threads. This works but has overhead. I try using mutex and condition_variable, but this not works,. especially for number searching threads>1.
My code:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
using namespace std;
const int NThr = 2;
struct InterchangeData {
bool endflag=false;
mutex chg_mutex;
condition_variable condition;
};
InterchangeData data;
bool execute1(InterchangeData* xchgData) {
for (int i=0; i<10; i++) {
if (xchgData->endflag) return false;
this_thread::sleep_for(chrono::milliseconds(rand()%10 +1 ));
if (rand()%100 == 50) {
lock_guard<mutex> lock(xchgData->chg_mutex);
if (!xchgData->endflag) {
printf("found!");
xchgData->endflag = true;
return true;
}
}
}
return false;
}
bool execute2(InterchangeData* xchgData) {
while (true) {
{
unique_lock<mutex> lock(xchgData->chg_mutex);
xchgData->condition.wait(lock);
}
int ret=2;
if (xchgData->endflag) ret=0;
if (execute1(xchgData)) ret=1;
{
unique_lock<mutex> lock(xchgData->chg_mutex);
xchgData->condition.notify_one();
this_thread::sleep_for(chrono::milliseconds(1));
if (ret==0) return false;
else if (ret==1) return true;
}
}
}
vector<thread*> threads;
typedef bool (*functype)(InterchangeData*);
void start(functype execute) {
for (int i=0; i<NThr; i++) {
auto t = new thread(execute, &data);
threads.push_back(t);
}
}
void stop() {
for (auto t : threads) {
t->join();
delete t;
}
threads.clear();
}
void test1() {
for (int i=0; i<10; i++) {
start(&execute1);
stop();
}
}
void test2() {
start(&execute2);
this_thread::sleep_for(chrono::milliseconds(100));
for (int i=0; i<10; i++) {
{
unique_lock<mutex> lock(data.chg_mutex);
data.condition.notify_one();
this_thread::sleep_for(chrono::milliseconds(1));
}
{
unique_lock<mutex> lock(data.chg_mutex);
data.condition.wait(lock);
}
}
{
unique_lock<mutex> lock(data.chg_mutex);
data.condition.notify_one();
this_thread::sleep_for(chrono::milliseconds(1));
}
stop();
}
int main() {
test2();
return 0;
}
Problem: is possible fast pausing/resuming execute function and main thread using condition_variable,unique_lock and wait/notify_one ?
I have a task on multi-threading in C++ in which there is a critical code block. Main requirements are the followings:
the critical code can be allowed to be re-entered (by the same or another thread) only after its current execution is finished,
UI thread mustn't be frozen while it is waiting for another thread currently executing the critical code.
So I created the following class in C++ Builder to fulfill the requirements.
Do you think there is any issue in it?
Many thanks for your time in advance!
Class declaration / definition:
#include "windows.h"
#include <vector>
#include <algorithm>
class TSyncObject
{
private:
DWORD WorkingThreadId;
std::vector<DWORD> WaitingThreadIds;
TCriticalSection *Section;
HANDLE Event;
//---------------------------------------------------------------------------
bool CanThreadWait(const DWORD ThreadId)
{
bool CanWait;
Section->Enter();
try
{
bool AlreadyWaiting =
std::find(WaitingThreadIds.begin(), WaitingThreadIds.end(), ThreadId) != WaitingThreadIds.end();
CanWait = !AlreadyWaiting && ThreadId != WorkingThreadId && WorkingThreadId;
if (CanWait)
{
WaitingThreadIds.push_back(ThreadId);
}
}
__finally
{
Section->Leave();
}
return CanWait;
}
//---------------------------------------------------------------------------
void Acquire(const DWORD ThreadId)
{
Section->Enter();
try
{
WorkingThreadId = ThreadId;
std::vector<DWORD>::iterator Pos =
std::find(WaitingThreadIds.begin(), WaitingThreadIds.end(), ThreadId);
if (Pos != WaitingThreadIds.end())
{
WaitingThreadIds.erase(Pos);
}
}
__finally
{
Section->Leave();
}
}
//---------------------------------------------------------------------------
void HandleError()
{
Section->Enter();
try
{
if (GetCurrentThreadId() == WorkingThreadId ||
(WaitingThreadIds.empty() && !WorkingThreadId))
{
WorkingThreadId = 0;
SetEvent(Event);
}
}
__finally
{
Section->Leave();
}
}
//---------------------------------------------------------------------------
public:
//---------------------------------------------------------------------------
enum TThreadAcquire {Acquired, WaitEjected, AppTerminated, Failed};
//---------------------------------------------------------------------------
TSyncObject() :
WorkingThreadId(0),
Section(new TCriticalSection()),
Event(CreateEventW(0, 0, 1, 0))
{
}
//---------------------------------------------------------------------------
virtual ~TSyncObject()
{
CloseHandle(Event);
delete Section;
}
//---------------------------------------------------------------------------
TThreadAcquire Acquire()
{
try
{
DWORD CurrentThreadId = GetCurrentThreadId();
if (WaitForSingleObject(Event, 0) != WAIT_OBJECT_0)
{
if (!CanThreadWait(CurrentThreadId))
{
return WaitEjected;
}
while (!Application->Terminated &&
(MsgWaitForMultipleObjects(1, &Event, 0, INFINITE, QS_ALLINPUT) - WAIT_OBJECT_0))
{
Application->ProcessMessages();
}
if (Application->Terminated)
{
return AppTerminated;
}
}
Acquire(CurrentThreadId);
return Acquired;
}
catch (...)
{
HandleError();
return Failed;
}
}
//---------------------------------------------------------------------------
void Release()
{
Section->Enter();
try
{
WorkingThreadId = 0;
SetEvent(Event);
}
__finally
{
Section->Leave();
}
}
//---------------------------------------------------------------------------
};
I will use it like this:
TSyncObject Sync; // Global for all threads
//...
A thread uses the object like this:
TSyncObject::TThreadAcquire Acq = Sync.Acquire();
try
{
if (Acq == TSyncObject::WaitEjected)
{
//...
return;
}
else if (Acq == TSyncObject::AppTerminated)
{
//...
return;
}
else if (Acq == TSyncObject::Failed)
{
//...
return;
}
// critical code block
}
__finally
{
Sync.Release();
}
I would suggest replacing all of the event and criticalsection handling with a simple semaphore instead.
Try something more like this:
MySyncObject.hpp
#ifndef MySyncObjectH
#define MySyncObjectH
#include "windows.h"
class TSyncObject
{
private:
HANDLE Semaphore;
public:
enum TThreadAcquire {Acquired, WaitEjected, AppTerminated, Failed};
TSyncObject();
~TSyncObject();
TThreadAcquire Acquire();
void Release();
};
#endif
MySyncObject.cpp
#include "MySyncObject.hpp"
bool __thread SyncState = 0;
TSyncObject::TSyncObject()
: Semaphore(NULL)
{
Semaphore = CreateSemaphore(NULL, 1, 1, NULL);
if (!Semaphore) RaiseLastOSError();
}
TSyncObject::~TSyncObject()
{
CloseHandle(Semaphore);
}
TSyncObject::TThreadAcquire TSyncObject::Acquire()
{
DWORD dwRet = WaitForSingleObject(Semaphore, 0);
if (dwRet == WAIT_TIMEOUT)
{
if (SyncState != 0)
return WaitEjected;
SyncState = 1;
while (!Application->Terminated)
{
dwRet = MsgWaitForMultipleObjects(1, &Semaphore, FALSE, INFINITE, QS_ALLINPUT);
if ((dwRet == WAIT_OBJECT_0) || (dwRet == WAIT_FAILED))
break;
if (dwRet == (WAIT_OBJECT_0+1))
{
try
{
Application->ProcessMessages();
}
catch (...)
{
//...
}
}
}
}
if (dwRet != WAIT_OBJECT_0)
{
SyncState = 0;
if (Application->Terminated)
return AppTerminated;
return Failed;
}
SyncState = 2;
return Acquired;
}
void TSyncObject::Release()
{
ReleaseSemaphore(Semaphore, 1, NULL);
SyncState = 0;
}
Then use it like this:
TSyncObject Sync;
...
TSyncObject::TThreadAcquire Acq = Sync.Acquire();
if (Acq == TSyncObject::Acquired)
{
try
{
// critical code block...
}
__finally
{
Sync.Release();
}
}
else
{
if (Acq == TSyncObject::WaitEjected)
{
//...
}
else if (Acq == TSyncObject::AppTerminated)
{
//...
}
else // TSyncObject::Failed
{
//...
}
return;
}
With that said, you might consider writing the class as a singleton instead of declaring a global variable:
MySyncObject.hpp
#ifndef MySyncObjectH
#define MySyncObjectH
#include "windows.h"
class TSyncObject
{
private:
HANDLE Semaphore;
TSyncObject();
public:
enum TThreadAcquire {Acquired, WaitEjected, AppTerminated, Failed};
~TSyncObject();
static TSyncObject& Instance();
TThreadAcquire Acquire();
void Release();
};
#endif
MySyncObject.cpp
#include "MySyncObject.hpp"
...
static TSyncObject& TSyncObject::Instance()
{
static TSyncObject inst;
return inst;
}
...
TSyncObject::TThreadAcquire Acq = TSyncObject::Instance().Acquire();
if (Acq == TSyncObject::Acquired)
{
try
{
// critical code block...
}
__finally
{
TSyncObject::Instance().Release();
}
}
else
{
if (Acq == TSyncObject::WaitEjected)
{
//...
}
else if (Acq == TSyncObject::AppTerminated)
{
//...
}
else // TSyncObject::Failed
{
//...
}
return;
}
Alternatively:
MySyncObject.hpp
#ifndef MySyncObjectH
#define MySyncObjectH
#include "windows.h"
class TSyncObject
{
private:
HANDLE Semaphore;
TSyncObject();
static TSyncObject& Instance();
public:
enum TThreadAcquire {Acquired, WaitEjected, AppTerminated, Failed};
~TSyncObject();
static TThreadAcquire Acquire();
static void Release();
};
#endif
MySyncObject.cpp
#include "MySyncObject.hpp"
...
TSyncObject::TThreadAcquire TSyncObject::Acquire()
{
TSyncObject &inst = Instance();
// use inst.Semaphore as needed...
}
void TSyncObject::Release()
{
TSyncObject &inst = Instance();
// use inst.Semaphore as needed...
}
TSyncObject::TThreadAcquire Acq = TSyncObject::Acquire();
if (Acq == TSyncObject::Acquired)
{
try
{
// critical code block...
}
__finally
{
TSyncObject::Release();
}
}
else
{
if (Acq == TSyncObject::WaitEjected)
{
//...
}
else if (Acq == TSyncObject::AppTerminated)
{
//...
}
else // TSyncObject::Failed
{
//...
}
return;
}
I have two pthreads one of them is reading from cin and putting it in a QUEUE and the other one is a worker thread checking the QUEUE every 2 seconds and printing something if there is something in it.
This is what's in my main:
#include <string>
#include <queue>
#include <iostream>
#include <stdio.h>
#include "Thread.h"
#include "Mutex.h"
using namespace std;
queue<string> lineup;
Mutex lock;
class InputReader:public Thread{
private:
string buffer;
protected:
virtual void run(){
while(true){
cout << "Please Enter some Text:\n" ;
getline(cin,buffer);
lock.lock();
lineup.push(buffer);
lock.unlock();
}
}
public:
InputReader(){}
~InputReader(){}
};
class Request: public Thread{
protected:
virtual void run(){
while(true){
sleep(2);
lock.lock();
if ((int)(lineup.size())>0){
cout << "Sending Request: " << lineup.front() << endl;
lineup.pop();
}
else{
cout << "Nothing to send!" <<endl;
}
lock.unlock();
}
}
public:
Request(){}
~Request(){}
};
int main(){
Request rq;InputReader iread;
iread.start(); rq.start();
iread.join(); rq.join();
return 0;
}
Where Thread.h and Thread.cpp are:
#ifndef __THREAD_H__
#define __THREAD_H__
#include <pthread.h>
class Thread
{
private:
pthread_t thread;
static void * dispatch(void *);
protected:
virtual void run() = 0;
public:
virtual ~Thread();
void start();
void join();
};
#endif
// THREAD.CPP
#include "Thread.h"
Thread::~Thread(){}
void * Thread::dispatch(void * ptr)
{
if (!ptr) return 0;
static_cast<Thread *>(ptr)->run();
pthread_exit(ptr);
return 0;
}
void Thread::start(){
pthread_create(&thread, 0, Thread::dispatch, this);
}
void Thread::join()
{
pthread_join(thread, 0);
}
Mutex.h and Mutex.cpp:
#ifndef __MUTEX_H__
#define __MUTEX_H__
#include <pthread.h>
class Mutex
{
private:
pthread_mutex_t mutex;
public:
Mutex();
~Mutex();
void lock();
void unlock();
bool trylock();
};
#endif
// MUTEX.CPP -----------------------
#include "Mutex.h"
Mutex::Mutex(){
pthread_mutex_init(&mutex, 0);
}
Mutex::~Mutex(){
pthread_mutex_destroy(&mutex);
}
void Mutex::lock(){
pthread_mutex_lock(&mutex);
}
void Mutex::unlock(){
pthread_mutex_unlock(&mutex);
}
bool Mutex::trylock() {
return (pthread_mutex_trylock(&mutex) == 0);
}
The problem is once its in the infinite loop waiting for stdin in the iread thread, the rq thread never starts. In fact, whichever .start() comes first is the one it gets stuck in... any ideas?
Turned out that I needed to run g++ with the -lpthread option. Does anyone know why this isn't on by default?