How would you set the object data that is shared between threads and needs to be updated once after the complete cycle of (say) two threads in busy loop?
CRITICAL_SECTION critical_section_;
int value; //needs to be updated once after the cycle of any number of threads running in busy loop
void ThreadsFunction(int i)
{
while (true)
{
EnterCriticalSection(&critical_section_);
/* Lines of Code */
LeaveCriticalSection(&critical_section_);
}
}
Edit: The value can be an object of any class.
Two suggestions:
Make the object itself thread safe.
Pass the object into the thread as instance data
I'll use C++ as a reference in my example. You can easily transpose this to pure C if you want.
// MyObject is the core data you want to share between threads
struct MyObject
{
int value;
int othervalue;
// all all the other members you want here
};
class MyThreadSafeObject
{
private:
CRITICAL_SECTION _cs;
MyObject _myojbect;
bool _fLocked;
public:
MyThreadSafeObject()
{
_fLocked = false
InitializeCriticalSection();
}
~MYThreadSafeObject()
{
DeleteCriticalSection();
}
// add "getter and setter" methods for each member in MyObject
int SetValue(int x)
{
EnterCriticalSection(&_cs);
_myobject.value = x;
LeaveCriticalSection(&_cs);
}
int GetValue()
{
int x;
EnterCriticalSection(&_cs);
x = _myobject.value;
LeaveCriticalSection(&_cs);
return x;
}
// add "getter and setter" methods for each member in MyObject
int SetOtherValue(int x)
{
EnterCriticalSection(&_cs);
_myobject.othervalue = x;
LeaveCriticalSection(&_cs);
}
int GetOtherValue()
{
int x;
EnterCriticalSection(&_cs);
x = _myobject.othervalue;
LeaveCriticalSection(&_cs);
return x;
}
// and if you need to access the whole object directly without using a critsec lock on each variable access, add lock/unlock methods
bool Lock(MyObject** ppObject)
{
EnterCriticalSection(&_cs);
*ppObject = &_myobject;
_fLocked = true;
return true;
}
bool UnLock()
{
if (_fLocked == false)
return false;
_fLocked = false;
LeaveCriticalSection();
return true;
}
};
Then, create your object and thread as follows:
MyThreadSafeObject* pObjectThreadSafe;
MyObject* pObject = NULL;
// now initilaize your object
pObjectThreadSafe->Lock(&pObject);
pObject->value = 0; // initailze value and all the other members of pObject to what you want them to be.
pObject->othervalue = 0;
pObjectThreadSafe->Unlock();
pObject = NULL;
// Create your threads, passing the pointer to MyThreadSafeObject as your instance data
DWORD dwThreadID = 0;
HANDLE hThread = CreateThread(NULL, NULL, ThreadRoutine, pObjectThreadSafe, 0, &dwThreadID);
And your thread will operate as follows
DWORD __stdcall ThreadFunction(void* pData)
{
MyThreadSafeObject* pObjectThreadSafe = (MyThreadSafeObject*)pData;
MyObject* pObject = NULL;
while (true)
{
/* lines of code */
pObjectThreadSafe->SetValue(x);
/* lines of code */
}
}
If you want implement thread safe update of an integer you should better use InterlockedIncrement and InterlockedDecrement or InterlockedExchangeAdd functions. See http://msdn.microsoft.com/en-us/library/ms684122(VS.85).aspx.
If you do need use EnterCriticalSection and LeaveCriticalSection you will find an example in http://msdn.microsoft.com/en-us/library/ms686908(v=VS.85).aspx, but I recommend you to use EnterCriticalSection inside of __try block and LeaveCriticalSection inside of the __finally part of this blocks.
Related
class Dummy
{
public:
int* A{};
int num{};
public:
Dummy(int num)
{
this->num = num;
A = new int[num];
}
~Dummy()
{
delete[] A;
}
};
Dummy* dummy()
{
Dummy* d = new Dummy{ 4 };
d->A[0] = 1;
d->A[1] = 2;
d->A[2] = 3;
d->A[3] = 4;
return d;
}
int main()
{
Dummy* ATT = dummy();
}
When I tired to run this program This is always showing Expection at destructor and program can't continue further. What's wrong in this Code...
Your code uses new() and delete() which nowadays in C++ should mostly never be used. Please make yourself comfortable with the appropriate pointer classes which handle memory management for you, like shared_ptr or unique_ptr.
I have implemented a custom storage interface in libtorrent as described in the help section here.
The storage_interface is working fine, although I can't figure out why readv is only called randomly while downloading a torrent. From my view the overriden virtual function readv should get called each time I call handle->read_piece in piece_finished_alert. It should read the piece for read_piece_alert?
The buffer is provided in read_piece_alert without getting notified in readv.
So the question is why it is called only randomly and why it's not called on a read_piece() call? Is my storage_interface maybe wrong?
The code looks like this:
struct temp_storage : storage_interface
{
virtual int readv(file::iovec_t const* bufs, int num_bufs
, int piece, int offset, int flags, storage_error& ec)
{
// Only called on random pieces while downloading a larger torrent
std::map<int, std::vector<char> >::const_iterator i = m_file_data.find(piece);
if (i == m_file_data.end()) return 0;
int available = i->second.size() - offset;
if (available <= 0) return 0;
if (available > num_bufs) available = num_bufs;
memcpy(&bufs, &i->second[offset], available);
return available;
}
virtual int writev(file::iovec_t const* bufs, int num_bufs
, int piece, int offset, int flags, storage_error& ec)
{
std::vector<char>& data = m_file_data[piece];
if (data.size() < offset + num_bufs) data.resize(offset + num_bufs);
std::memcpy(&data[offset], bufs, num_bufs);
return num_bufs;
}
virtual bool has_any_file(storage_error& ec) { return false; }
virtual ...
virtual ...
}
Intialized with
storage_interface* temp_storage_constructor(storage_params const& params)
{
printf("NEW INTERFACE\n");
return new temp_storage(*params.files);
}
p.storage = &temp_storage_constructor;
The function below sets up alerts and invokes read_piece on each completed piece.
while(true) {
std::vector<alert*> alerts;
s.pop_alerts(&alerts);
for (alert* i : alerts)
{
switch (i->type()) {
case read_piece_alert::alert_type:
{
read_piece_alert* p = (read_piece_alert*)i;
if (p->ec) {
// read_piece failed
break;
}
// piece buffer, size is provided without readv
// notification after invoking read_piece in piece_finished_alert
break;
}
case piece_finished_alert::alert_type: {
piece_finished_alert* p = (piece_finished_alert*)i;
p->handle.read_piece(p->piece_index);
// Once the piece is finished, we read it to obtain the buffer in read_piece_alert.
break;
}
default:
break;
}
}
Sleep(100);
}
I will answer my own question. As Arvid said in the comments: readv was not invoked because of caching. Setting settings_pack::use_read_cache to false will invoke readv always.
I'm trying to use move semantics (just as an experiment).
Here is my code:
class MyClass {
public:
MyClass(size_t c): count(c) {
data = new int[count];
}
MyClass( MyClass&& src) : count(src.count) {
data = src.data;
src.count = 0;
src.data = nullptr;
}
void operator=( MyClass&& src) {
data = src.data;
count = src.count;
src.count = 0;
src.data = nullptr;
}
~MyClass() {
if (data != nullptr)
delete[] data;
}
int* get_data() const {
return data;
}
size_t get_count() const {
return count;
}
private:
MyClass(const MyClass& src) : count(src.count) {
data = new int[src.count];
memcpy(data, src.data, sizeof(int)*src.count);
}
void operator=(const MyClass& src) {
count = src.count;
data = new int[src.count];
memcpy(data, src.data, sizeof(int)*src.count);
}
int* data;
size_t count;
};
int main()
{
MyClass mc(150);
for (size_t i = 0; i < mc.get_count(); ++i)
mc.get_data()[i] = i;
MyClass &&mc2 = std::move(mc);
return 0;
}
But std::move does not move mc to mc2, it just copies (copyies pointer as it is). If I remove copy constructor compiler generates it for MyClass.
How can I force move semantics to be used? How can I make it to be used in such constructions:
MyClass mc2(mc); //Move, not copy
-or-
MyClass mc2 = mc; //Move, not copy
I tried to use a '&&' operator to explicitely mark rvalue, but, of cause, it didn't work.
You're declaring m2 as a reference, not as a value. So it still refers to what it was initialised with, namely m1. You wanted this:
MyClass mc2 = std::move(mc);
Live example
As for the second part - there is no way to force a construct like these:
MyClass mc2(mc); //Move, not copy
//-or-
MyClass mc2 = mc; //Move, not copy
to move. If you want to move from an lvalue (and mc is indeed an lvalue), you have to use std::move (or another cast to rvalue) explicitly.
There is one thing you could do, but it would be a dirty hack, make the code unintuitive and be a great source for bugs. You could add an overload of the copy constructor (and copy assignment operator) taking a non-const reference, which would do the move. Basically something like std::auto_ptr used to do before it was rightfully deprecated. But it would never pass code review with me, for example. If you want to move, just std::move.
A few side notes:
Calling delete or delete[] on a null pointer is guaranteed to be a no-op, so you can safely drop the if from your destructor.
It's generally preferable to use std::copy instead of memcpy in C++ code, you don't have to worry about getting the sizeof right
You can force move semantics, if you delete the copy constructor and the assignment operator
MyClass(const MyClass& src)= delete;
void operator=(const MyClass& src) = delete;
in this case the provided move constructor or move assignment operator will be picked.
Rewrite your class a bit with some comments. Look over it, you might notice a few things you missed. Like:
in MyClass(size_t c) not checking for c != 0.
in void operator=(const MyClass& src) not delete[] data; (if exists) before reallocating.
And some other tiny details.Hope your compiler can handle this.
class MyClass {
private:
// initialize memebers directly
int* data = nullptr;
size_t count = 0;
public:
// default empty contructor
MyClass() = default;
// destructor
~MyClass() {
*this = nullptr; // use operator = (nullptr_t)
}
// allow nullptr construct
MyClass(nullptr_t):MyClass() {}
// allow nullptr assignment (for clearing)
MyClass& operator = (nullptr_t) {
if(data) {
delete[] data;
data = nullptr;
}
count = 0;
return *this;
}
// chain to default constructor, redundant in this case
MyClass(size_t c):MyClass() {
// maybe size_t is 0?
if(count = c) {
data = new int[count];
}
}
// chain to default constructor, redundant in this case
MyClass(MyClass&& src):MyClass() {
*this = std::move(src); // forward to move assignment
}
MyClass& operator=(MyClass&& src) {
// don't swap with self
if(&src != this) {
// it's better to swap and let src destroy when it feels like it.
// I always write move contructor and assignment to swap data.
// it's gonna be destroyed anyway, or not...
std::swap(src.data, data);
std::swap(src.count, count);
}
return *this;
}
MyClass(const MyClass& src):MyClass() {
*this = src; // forward to copy assignment
}
MyClass& operator = (const MyClass& src) {
// don't copy to self
if(&src != this) {
// delete first
if(data) {
delete[] data;
data = nullptr;
}
// now reallocate
if(count = src.count) {
data = new int[count];
memcpy(data, src.data, sizeof(int)* count);
}
}
return *this;
}
// easy way to use the object in a if(object) to test if it has content
explicit operator bool() const {
return data && count;
}
// same as above but made for if(!object) to test if empty
bool operator !() const {
return !data || !count;
}
public:
int* get_data() const {
return data;
}
size_t get_count() const {
return count;
}
// add more custom methods
};
Now to move you do this:
MyClass object1; // default construct
MyClass object1(5); // construct with capacity
MyClass object2(object1); // copy constructor
MyClass object3(std::move(object1)); // move constructor
object2 = object1; // copy assignment
object3 = std::move(object1); // move constructor
std::swap(object2, object3); // swap the two
object2 = nullptr; // to empty it
if(object1); // bool cast
Question
What can I do to get a locking mechanism that provides minimal and stable latency while guaranteeing that a thread cannot reacquire a resource before another thread has acquired and released it?
The desirability of answers to this question are ranked as follows:
Some combination of built-in C++11 features that work in MinGW on Windows 7 (note that the <thread> and <mutex> libraries do not work on a Windows platform)
Some combination of Windows API features
A modification to the FairLock listed below, my own attempt at implementing such a mechanism
Some features provided by a free, open-source library that does not require a .configure/make/make install process, (getting that to work in MSYS is more of an adventure than I care for)
Background
I am writing an application which is effectively a multi-stage producer/consumer. One thread generates input consumed by another thread, which produces output consumed by yet another thread. The application uses pairs of buffers so that, after an initial delay, all threads can work nearly simultaneously.
Since I am writing a Windows 7 application, I had been using CriticalSections to guard the buffers. The problem with using CriticalSections (or, so far as I can tell, any other Windows or C++11-built-in synchronization object) is that it does not allow for any provision that a thread that just released a lock cannot reacquire it until another thread has done so first. Because of this, many of my test drivers for the middle thread (the Encoder) never gave the Encoder a chance to acquire the test input buffers and completed without having tested them. The end result was a ridiculous process of trying to determine an artificial wait time that stochastically worked for my machine.
Since the structure of my application requires that each stage waits for the other stage to have acquired, finished using, and released the necessary buffers for getting to use the buffer again, I need, for lack of a better term, a fair locking mechanism. I took a crack at writing one (the source code is provided below). In testing, this FairLock allows my test driver to run my Encoder at the same speeds that I was able to achieve using the CriticalSection maybe 60% of the runs. The other 40% of the runs take anywhere between 10 to 100 ms longer, which is not acceptable for my application.
FairLock
// FairLock.hpp
#ifndef FAIRLOCK_HPP
#define FAIRLOCK_HPP
#include <atomic>
using namespace std;
class FairLock {
private:
atomic_bool owned {false};
atomic<DWORD> lastOwner {0};
public:
FairLock(bool owned);
bool inline hasLock() const;
bool tryLock();
void seizeLock();
void tryRelease();
void waitForLock();
};
#endif
// FairLock.cpp
#include <windows.h>
#include "FairLock.hpp"
#define ID GetCurrentThreadId()
FairLock::FairLock(bool owned) {
if (owned) {
this->owned = true;
this->lastOwner = ID;
} else {
this->owned = false;
this->lastOwner = 0;
}
}
bool inline FairLock::hasLock() const {
return owned && lastOwner == ID;
}
bool FairLock::tryLock() {
bool success = false;
DWORD id = ID;
if (owned) {
success = lastOwner == id;
} else if (
lastOwner != id &&
owned.compare_exchange_strong(success, true)
) {
lastOwner = id;
success = true;
} else {
success = false;
}
return success;
}
void FairLock::seizeLock() {
bool success = false;
DWORD id = ID;
if (!(owned && lastOwner == id)) {
while (!owned.compare_exchange_strong(success, true)) {
success = false;
}
lastOwner = id;
}
}
void FairLock::tryRelease() {
if (hasLock()) {
owned = false;
}
}
void FairLock::waitForLock() {
bool success = false;
DWORD id = ID;
if (!(owned && lastOwner == id)) {
while (lastOwner == id); // spin
while (!owned.compare_exchange_strong(success, true)) {
success = false;
}
lastOwner = id;
}
}
EDIT
DO NOT USE THIS FairLock CLASS; IT DOES NOT GUARANTEE MUTUAL EXCLUSION!
I reviewed the above code to compare it against The C++ Programming Language: 4th Edition text I had not read carefully and what CouchDeveloper's recommended Synchronous Queue. I realized that there are several sequences in which the thread that just released the FairLock can be tricked into thinking it still owns it. All it takes is interleaving instructions as follows:
New owner: set owned to true
Old owner: is owned true? yes
Old owner: am I the last owner? yes
New owner: set me as the last owner
At this point, the old and new owners both enter their critical sections.
I am considering whether this problem has a solution and whether it is worth attempting to solve this at all. In the meantime, don't use this unless you see a fix.
I would implement this in C++11 using a condition_variable-per-thread setup so that I could choose exactly which thread to wake up when (Live demo at Coliru):
class FairMutex {
private:
class waitnode {
std::condition_variable cv_;
waitnode* next_ = nullptr;
FairMutex& fmtx_;
public:
waitnode(FairMutex& fmtx) : fmtx_(fmtx) {
*fmtx.tail_ = this;
fmtx.tail_ = &next_;
}
~waitnode() {
for (waitnode** p = &fmtx_.waiters_; *p; p = &(*p)->next_) {
if (*p == this) {
*p = next_;
if (!next_) {
fmtx_.tail_ = &fmtx_.waiters_;
}
break;
}
}
}
void wait(std::unique_lock<std::mutex>& lk) {
while (fmtx_.held_ || fmtx_.waiters_ != this) {
cv_.wait(lk);
}
}
void notify() {
cv_.notify_one();
}
};
waitnode* waiters_ = nullptr;
waitnode** tail_ = &waiters_;
std::mutex mtx_;
bool held_ = false;
public:
void lock() {
auto lk = std::unique_lock<std::mutex>{mtx_};
if (held_ || waiters_) {
waitnode{*this}.wait(lk);
}
held_ = true;
}
bool try_lock() {
if (mtx_.try_lock()) {
std::lock_guard<std::mutex> lk(mtx_, std::adopt_lock);
if (!held_ && !waiters_) {
held_ = true;
return true;
}
}
return false;
}
void unlock() {
std::lock_guard<std::mutex> lk(mtx_);
held_ = false;
if (waiters_ != nullptr) {
waiters_->notify();
}
}
};
FairMutex models the Lockable concept so it can be used like any other standard library mutex type. Put simply, it achieves fairness by inserting waiters into a list in arrival order, and passing the mutex to the first waiter in the list when unlocking.
If it's useful:
This demonstrates *) an implementation of a "synchronous queue" using semaphores as synchronization primitives.
Note: the actually implementation uses semaphores implemented with GCD (Grand Central Dispatch):
using gcd::mutex;
using gcd::semaphore;
// A blocking queue in which each put must wait for a get, and vice
// versa. A synchronous queue does not have any internal capacity,
// not even a capacity of one.
template <typename T>
class simple_synchronous_queue {
public:
typedef T value_type;
enum result_type {
OK = 0,
TIMEOUT_NOT_DELIVERED = -1,
TIMEOUT_NOT_PICKED = -2,
TIMEOUT_NOTHING_OFFERED = -3
};
simple_synchronous_queue()
: sync_(0), send_(1), recv_(0)
{
}
void put(const T& v) {
send_.wait();
new (address()) T(v);
recv_.signal();
sync_.wait();
}
result_type put(const T& v, double timeout) {
if (send_.wait(timeout)) {
new (storage_) T(v);
recv_.signal();
if (sync_.wait(timeout)) {
return OK;
}
else {
return TIMEOUT_NOT_PICKED;
}
}
else {
return TIMEOUT_NOT_DELIVERED;
}
}
T get() {
recv_.wait();
T result = *address();
address()->~T();
sync_.signal();
send_.signal();
return result;
}
std::pair<result_type, T> get(double timeout) {
if (recv_.wait(timeout)) {
std::pair<result_type, T> result =
std::pair<result_type, T>(OK, *address());
address()->~T();
sync_.signal();
send_.signal();
return result;
}
else {
return std::pair<result_type, T>(TIMEOUT_NOTHING_OFFERED, T());
}
}
private:
using storage_t = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
T* address() {
return static_cast<T*>(static_cast<void*>(&storage_));
}
storage_t storage_;
semaphore sync_;
semaphore send_;
semaphore recv_;
};
*) demonstrates: be carefully about potential issues, could be improved, etc. ... ;)
I accepted CouchDeveloper's answer since it pointed me down the right path. I wrote a Windows-specific C++11 implementation of a synchronous queue, and added this answer so that others could consider/use it if they so choose.
// SynchronousQueue.hpp
#ifndef SYNCHRONOUSQUEUE_HPP
#define SYNCHRONOUSQUEUE_HPP
#include <atomic>
#include <exception>
#include <windows>
using namespace std;
class CouldNotEnterException: public exception {};
class NoPairedCallException: public exception {};
template typename<T>
class SynchronousQueue {
private:
atomic_bool valueReady {false};
CRITICAL_SECTION getCriticalSection;
CRITICAL_SECTION putCriticalSection;
DWORD wait {0};
HANDLE getSemaphore;
HANDLE putSemaphore;
const T* address {nullptr};
public:
SynchronousQueue(DWORD waitMS): wait {waitMS}, address {nullptr} {
initializeCriticalSection(&getCriticalSection);
initializeCriticalSection(&putCriticalSection);
getSemaphore = CreateSemaphore(nullptr, 0, 1, nullptr);
putSemaphore = CreateSemaphore(nullptr, 0, 1, nullptr);
}
~SynchronousQueue() {
EnterCriticalSection(&getCriticalSection);
EnterCriticalSection(&putCriticalSection);
CloseHandle(getSemaphore);
CloseHandle(putSemaphore);
DeleteCriticalSection(&putCriticalSection);
DeleteCriticalSection(&getCriticalSection);
}
void put(const T& value) {
if (!TryEnterCriticalSection(&putCriticalSection)) {
throw CouldNotEnterException();
}
ReleaseSemaphore(putSemaphore, (LONG) 1, nullptr);
if (WaitForSingleObject(getSemaphore, wait) != WAIT_OBJECT_0) {
if (WaitForSingleObject(putSemaphore, 0) == WAIT_OBJECT_0) {
LeaveCriticalSection(&putCriticalSection);
throw NoPairedCallException();
} else {
WaitForSingleObject(getSemaphore, 0);
}
}
address = &value;
valueReady = true;
while (valueReady);
LeaveCriticalSection(&putCriticalSection);
}
T get() {
if (!TryEnterCriticalSection(&getCriticalSection)) {
throw CouldNotEnterException();
}
ReleaseSemaphore(getSemaphore, (LONG) 1, nullptr);
if (WaitForSingleObject(putSemaphore, wait) != WAIT_OBJECT_0) {
if (WaitForSingleObject(getSemaphore, 0) == WAIT_OBJECT_0) {
LeaveCriticalSection(&getCriticalSection);
throw NoPairedCallException();
} else {
WaitForSingleObject(putSemaphore, 0);
}
}
while (!valueReady);
T toReturn = *address;
valueReady = false;
LeaveCriticalSection(&getCriticalSection);
return toReturn;
}
};
#endif
i have implemented my ClientSocket class from CAsyncSocket:
class ClientSocket : public CAsyncSocket
{
// this socket sends data back to "backSocket" which points to this only for
// testing but it can send data to other sockets like that too.
ClientSocket * backSocket;
// store some data in backupData untill connection is established.
StringBuilder * backupData;
public:
virtual void OnClose(int);
virtual void OnReceive(int);
ClientSocket(void);
bool ConnectToBACK();
virtual ~ClientSocket(void);
};
ClientSocket::ClientSocket(void)
{
// DONOT run to back !!! recursive calls otherwise.
backSocket = NULL;
backupData = NULL;
}
bool ClientSocket::ConnectToBACK()
{
if(this->backSocket != NULL)
return true;
// just for debugging :)
this->backSocket = this;
return true;
}
ClientSocket::~ClientSocket(void)
{
this->Close();
if(this->backSocket)
{
this->backSocket->Close();
delete this->backSocket;
this->backSocket = NULL;
}
}
void ClientSocket::OnClose(int nErrorCode)
{
if(this->backSocket != NULL)
{
this->backSocket->Close();
}
CAsyncSocket::OnClose(nErrorCode);
}
void ClientSocket::OnReceive(int nErrorCode)
{
if(nErrorCode == 0)
{
char *buffer = new char[2049];
int bufLen = sizeof(buffer)/sizeof(buffer[0]);
int received = this->Receive(buffer, bufLen-1, 0);
if(received == SOCKET_ERROR)
{
return ;
}
if(this->ConnectToback())
{
if(backupData)
{
int backupLen;
char *backup = backupData->ToString(&backupLen);
this->backSocket->Send(backup, backupLen);
delete backupData;
delete [] backup;
backupData = NULL;
}
this->backSocket->Send(buffer, received);
delete buffer;
}
else
{
if(backupData == NULL)
{
backupData = new StringBuilder();
}
backupData->Insert(buffer, received);
}
}
CAsyncSocket::OnReceive(nErrorCode);
}
I have not associated any GUI to this as i thought that it would be good for no overheads.
I donot require it. I have also done AfxSocketIback() in main and from a thread started another ListeningSocket .
netstat -a shows proper binding at the port of ListeningSocket and status as Listening
// ListeningSocket inherits public CAsyncSocket
void ListeningSocket::OnAccept(int nErrorCode)
{
#ifdef DEBUG
std::cout << "\nOnAccepting Proxy Server :)";
#endif
if(nErrorCode == 0)
{
ClientSocket *FromCliet = new ClientSocket();
FromCliet->value = 100;
if(this->Accept(*FromCliet, NULL, NULL))
{
// Connection just has ClientSocket * client
Connection * connection = new Connection(FromCliet);
// a list<Connection *> is stored in ListeningSocket
this->clients.push_front(connection);
}
else
{
std::cerr << "\nFailed to accept connection from Client";
}
}
CAsyncSocket::OnAccept(nErrorCode);
}
When putting brakepoints in ListenSocket::OnAccept, it never comes here.
EDIT:
static DWORD WINAPI StartListening(LPVOID param)
{
ListeningSocket *app = (ListeningSocket *)param;
if(false == app->Create(7897, SOCK_STREAM, 31, "127.0.0.1"))
{
std::cerr << "\nCould not create\bind to port";
delete app;
return -1;
}
if(false == app->Listen())
{
std::cerr << "\nCould not listen";
app->Close();
delete app;
return -1;
}
return 0;
}
int ListeningSocket::Start()
{
if(NULL == CreateThread(NULL,0, StartListening, (LPVOID)this,0, NULL))
{
return -1;
}
return 0;
}
I have NOT made it like MFC Wizard solution. I have simple project and main().
My ListeningSocket Class is Singletone Class:
class ListeningSocket : public CAsyncSocket
{
private:
static ListeningSocket * ListeningSocket;
std::list<Connection *> clients;
ListeningSocket(void);
public:
// overrides
virtual void OnAccept(int);
virtual void OnClose(int);
static ListeningSocket * GetListeningSocket();
virtual ~ListeningSocket(void);
virtual void Close();
int Start(void);
};
CAsyncSocket class internally uses Windows messages for firing events. You need to create CAsyncSocket-derived class in a thread with message loop. In this case events will be called. Pseudo-code:
// This function runs in the context of worker thread
void MyClass::ThreadFunction()
{
mySocket.Create(...); // creating CAsyncSocket-derived class
// Run message loop.
BOOL bRes = FALSE;
MSG msg;
while((bRes = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRes == -1)
{
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
To stop this thread, use PostQuitMessage function.
Edit.
I didn't post all multi-threading details, assuming that you are familiar with them. Generally, CreateThread requires global function as parameter (or class static function). To call regular class method, use "this" as CreateThread parameter, which is passed as void* to global thread function. Cast it back to the class pointer and call regular class method.
I've also run into this same issue - with someone's re-implementation of CAsyncSocket called CAsyncSocketEx -- built to be a function replacement for CAsyncSocket.
Just this week, I thought I'd see if I could use this code again, and I ran into this very same problem. Since the async-window was not created inside a thread that has a message loop, the events were not firing from WSAAsyncSelect()...
see this: http://support.microsoft.com/kb/90975