0

My code calls a function from 3rd party library. Let's call this function SomeFunc

void SomeFunc(void (*callBack) (int));

As you can see SomeFunc takes a callback function parameter. Once SomeFunc is called, the calling thread will progress, and the library will execute the callback several time on a different thread -- passing it different status code.

My requirement is the thread that calls SomeFunc (aka main thread) should wait until certain status code is passed to the callback. So far I have something like this

CEvent *pEvt = NULL;

void myCallBack(int code) {
  if(code == SOME_MAGIC_NUM) pEvt->SetEvent(); // signal thread waiting for this event obj they can continue
}

int main (int argc, char** argv) {
  pEvt = new CEvent(FALSE, TRUE);
  SomeFunc(myCallBack); // This doesn't block, main thread will progress to next line
  WaitForSingleObject(pEvt, 5000); // wait here until 3rd party library call myCallBack with code SOME_MAGIC_NUM -- or if it doesn't after 5 seconds, continue

  // do interesting stuff here..

  return EXIT_SUCCESS;
}

Now this seem fine if I only do this on the main thread / main function like above. However if multiple thread can execute the code block in main above, my problem is they will share reference to the global pEvt variable, and it will mess up

What's the best code design approach I should take here? Ideally I would like to change the callback function signature to accept reference to the CEvent object, but since it's a 3rd party library I'm unable to do that.

gerrytan
  • 40,313
  • 9
  • 84
  • 99
  • It isn't clear as presented: Is `SomeFunc()` spawning your secondary threads and then returning back to its caller (in this case `main()`) ? I only ask because you proposed this in a multi-threaded context, then proceed to show code with no multi-threading at all. – WhozCraig Apr 02 '13 at 06:15
  • Correct, SomeFunc() spawned secondary thread -- let's call this "Thread X". And from Thread X it will call myCallBack passing it different code parameter. The main thread and Thread X will execute concurrently, that's why I'm using `WaitForSingleObject(pEvt, 5000)` above to halt the main thread until I'm happy to progress – gerrytan Apr 02 '13 at 06:21
  • May I ask why you would need multiple threads to monitor a single callback event? – jwalk Apr 02 '13 at 06:26
  • No matter what then, you're introducing a race condition in the followup code. As written there is no guarantee that `main()` will not complete before the thread managed by the library implementing SomeFunc() does. Furthermore, 5000 because...? 4999 wasn't enough and 5001 was just too much? Your comment describing the wait in-code is not accurate. It should read: "wait here until 3rd party library call myCallBack with code SOME_MAGIC_NUM, **or I just don't care after approximately 5 seconds.**" But I postulate you *do* care. – WhozCraig Apr 02 '13 at 06:27
  • @jwalk, the code is part of a web service implementation. Everytime a client is making a request, response will be served on a separate worker thread, but I'm afraid the global variable pEvt is shared on all worker thread. If 2 clients are calling the web service simulataneously, the concurrency problem will happen. I've considered passing class member as a callback but C++ has a different function pointer type for that and it won't compile. – gerrytan Apr 02 '13 at 06:29
  • @WhozCraig correct, thanks for that. I have edited the comment on the code to reflect this. The code above is a simpler version so I can easily present the problem. In the real code I throw an error if main thread does not progress after some timeout value – gerrytan Apr 02 '13 at 06:30
  • @gerrytan so is there a reason you need a single global `CEvent`, instead of constructing a new one per-request? Perhaps I'm unclear on the 3rd-party async operation / what you're trying to achieve by using a shared global? – jwalk Apr 02 '13 at 06:39
  • @jwalk I can create a new one per request, but I don't know how can I pass the reference to it to the callback function, since the call back function signature is already predefined on a 3rd party library – gerrytan Apr 02 '13 at 08:21

2 Answers2

0

You really want the equivalent of a closure in javascript. You can accomplish something similar by binding the function call to a new CEvent object every time. Take a look at std::bind1st or boost::bind.

See also this stackoverflow thread

Community
  • 1
  • 1
hifier
  • 457
  • 3
  • 9
0

You can only achieve this is the 3rd party provides a way to pass back a 'custom' argument to the callback. Well designed APIs allow to set up a callback and a void* value and the callback receives this argument when invoked. From this void* you can expand to anything you like, including objects and method calls, via unsafe casting. All solutions based on binding or member function address or whatever else ultimately boil down to the same issue: somehow the this* has to be passed back to the callback.

For an example, see BIO_set_callback: it allows you to set the callback and a callback arbitrary argument. Note that the callabck argument can be indirect, like for example in gnutls: the argument can be set as arbitrary data on the session via gnutls_session_set_ptr and then in the callback(s) it can be retrieved using gnutls_session_get_ptr. Your 3rd party may provide such an indirect method.

If the library does not offer such feature then you're stranded into hacks. For example you can have a collection of callbacks 'available', each one associated with a specific event (ie. different functions as address, although same code). You pick one callback and remove it from collection and place it, then wait for the event associated with that callback. When done, place the callback back into the available list. The size of the 'available' list is hard coided at compile time as you really need separate functions, one for each callback.

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • Not true. This is what binding is for. – hifier Apr 02 '13 at 06:58
  • @hifier: pray tell how does the binding receive the hidden `this*` argument? – Remus Rusanu Apr 02 '13 at 07:01
  • [like this](http://www.boost.org/doc/libs/1_53_0/libs/bind/bind.html#with_member_pointers). – hifier Apr 02 '13 at 07:05
  • That passes `this*` as a copy all the way to the call. **Only works with C++**. Show an example of a plain C callback with the signature `void f(int)`, as in OP. – Remus Rusanu Apr 02 '13 at 07:06
  • ... by "hidden `this*` argument" I assume you're asking how you can bind a functor to method on a specific instance of a class. – hifier Apr 02 '13 at 07:07
  • The title of this question is "C++ concurrency, synchronization design to avoid multiple execution issue". Why are we limited to C? – hifier Apr 02 '13 at 07:08
  • Because it makes all the difference. If the 3rd party callback signature is `int f(void)` then you cannot give an answer that uses bindings because **it just not going to work**. Is a trivial answer that does not address the real problem. – Remus Rusanu Apr 02 '13 at 07:13
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/27383/discussion-between-hifier-and-remus-rusanu) – hifier Apr 02 '13 at 07:20