Pthreads create argument passing - pthreads

I am trying to pass 2 arguments using a struct in the pthread_create method. One argument is for the total amount of threads active and the other one is for the id of the thread, which is the number it receives from theloop (i). The problem i have right now is that the argument_struct.id value has the same value when i use the run the for loop.
struct argument_struct {
int total_threads;
int id;
} argument_struct;
void * body(void *args)
{
struct argument_struct *arguments = args;
printf("Hello World! %i, %i \n", argument_struct.total_threads, argument_struct.id);
return NULL;
}
int main()
{
const num_threads = 20;
pthread_t thread[num_threads];
pthread_attr_t attr;
int i;
/* Initiate the thread attributes */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
argument_struct.total_threads = num_threads;
for (i=0; i<num_threads; i++)
{
argument_struct.id = i;
pthread_create(&thread[i], &attr, &body, (void *)&argument_struct);
}
output
Hello world! 20, 19
Hello world! 20, 19
Hello world! 20, 19
etc

You have a race condition - by the time a thread gets to look at the structure it has been passed, the main thread has already reused that structure to pass to the next created thread.
There are several ways you could solve this:
1) have an array of struct argument_struct items (the array at least large enough for the number of threads), and use a separate element to pass to each newly created thread
2) store the total_threads value in a global that the threads can read. There's no data race here, since the value isn't updated after it is initialized and the threads will only read it. On the other hand you have a shared global which isn't a great idea to use as an interface, but might be OK for small programs. To avoid the problem with sharing the id element, just pass it directly by casting it to a (void*).
3) dynamically allocate a new struct argument_struct to pass to each thread. The thread will become the owner of the struct and will be responsible for freeing it. This is probably the best solution generally (ie., the technique will work well even if you start passing in a large number or complex set of arguments).
4) Use something like a semaphore or condition variable to let the thread signal when it has finished using the passed in structure. The main thread should wait on that synchronization object before reusing the structure to create the next thread. This might be overkill for your simple example, but again it'll work in situations of greater complexity (though I still think that passing in a dynamically allocated structure is simpler).

You are passing each thread the same argument, which is why they all see the same value.

Related

pthread_cond_signal does not wake up the thread that waits

I am writing a program which creates a thread that prints 10 numbers. When it prints 5 of them, it waits and it is notifying the main thread and then it continues for the next 5 numbers
This is test.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
int rem = 10;
int count = 5;
pthread_mutex_t mtx;
pthread_cond_t cond1;
pthread_cond_t cond2;
void *f(void *arg)
{
int a;
srand(time(NULL));
while (rem > 0) {
a = rand() % 100;
printf("%d\n",a);
rem--;
count--;
if (count==0) {
printf("time to wake main thread up\n");
pthread_cond_signal(&cond1);
printf("second thread waits\n");
pthread_cond_wait(&cond2, &mtx);
printf("second thread woke up\n");
}
}
pthread_exit(NULL);
}
int main()
{
pthread_mutex_init(&mtx, 0);
pthread_cond_init(&cond1, 0);
pthread_cond_init(&cond2, 0);
pthread_t tids;
pthread_create(&tids, NULL, f, NULL);
while(1) {
if (count != 0) {
printf("main: waiting\n");
pthread_cond_wait(&cond1, &mtx);
printf("5 numbers are printed\n");
printf("main: waking up\n");
pthread_cond_signal(&cond2);
break;
}
pthread_cond_signal(&cond2);
if (rem == 0) break;
}
pthread_join(tids, NULL);
}
The output of the program is:
main: waiting
//5 random numbers
time to wake main thread up
second thread waits
5 numbers are printed
main: waking up
Since I do pthread_cond_signal(&cond2);I thought that the thread will wake up and prints the rest numbers but this is not the case. Any ideas why? Thanks in advance.
Summary
The issues have been summarized in comments, or at least most of them. So as to put an actual answer on record, however:
Pretty much nothing about the program's use of shared variables and synchronization objects is correct. It's behavior is undefined, and the specific manifestation observed is just one of the more likely in a universe of possible behaviors.
Accessing shared variables
If two different threads access (read or write) the same non-atomic object during their runs, and at least one of the accesses is a write, then all accesses must be properly protected by synchronization actions.
There is a variety of these, too large to cover comprehensively in a StackOverflow answer, but among the most common is to use a mutex to guard access. In this approach, a mutex is created in the program and designated for protecting access to one or more shared variables. Each thread that wants to access one of those variables locks the mutex before doing so. At some later point, the thread unlocks the mutex, lest other threads be permanently blocked from locking the mutex themselves.
Example:
pthread_mutex_t mutex; // must be initialized before use
int shared_variable;
// ...
void *thread_one_function(void *data) {
int rval;
// some work ...
rval = pthread_mutex_lock(&mutex);
// check for and handle lock failure ...
shared_variable++;
// ... maybe other work ...
rval = pthread_mutex_unlock(&mutex);
// check for and handle unlock failure ...
// more work ...
}
In your program, the rem and count variables are both shared between threads, and access to them needs to be synchronized. You already have a mutex, and using it to protect accesses to these variables looks like it would be appropriate.
Using condition variables
Condition variables have that name because they are designed to support a specific thread interaction pattern: that one thread wants to proceed past a certain point only if a certain condition, which depends on actions performed by other threads, is satisfied. Such requirements arise fairly frequently. It is possible to implement this via a busy loop, in which the thread repeatedly tests the condition (with proper synchronization) until it is true, but this is wasteful. Condition variables allow such a thread to instead suspend operation until a time when it makes sense to check the condition again.
The correct usage pattern for a condition variable should be viewed as a modification and specialization of the busy loop:
the thread locks a mutex guarding the data on which the condition is to be computed;
the thread tests the condition;
if the condition is satisfied then this procedure ends;
otherwise, the thread waits on a designated condition variable, specifying the (same) mutex;
when the thread resumes after its wait, it loops back to (2).
Example:
pthread_cond_t cv; // must be initialized before use
void *thread_two_function(void *data) {
int rval;
// some work ...
rval = pthread_mutex_lock(&mutex);
// check for and handle lock failure ...
while (shared_variable < 5) {
rval = pthread_cond_wait(&cv, &mutex);
// check for and handle wait failure ...
}
// ... maybe other work ...
rval = pthread_mutex_unlock(&mutex);
// check for and handle unlock failure ...
// more work ...
}
Note that
the procedure terminates (at (3)) with the thread still holding the mutex locked. The thread has an obligation to unlock it, but sometimes it will want to perform other work under protection of that mutex first.
the mutex is automatically released while the thread is waiting on the CV, and reacquired before the thread returns from the wait. This allows other threads the opportunity to access shared variables protected by the mutex.
it is required that the thread calling pthread_cond_wait() have the specified mutex locked. Otherwise, the call provokes undefined behavior.
this pattern relies on threads to signal or broadcast to the CV at appropriate times to notify any then-waiting other threads that they might want to re-evaluate the condition for which they are waiting. That is not modeled in the examples above.
multiple CVs can use the same mutex.
the same CV can be used in multiple places and with different associated conditions. It makes sense to do this when all the conditions involved are affected by the same or related actions by other threads.
condition variables do not store signals. Only threads that are already blocked waiting for the specified CV are affected by a pthread_cond_signal() or pthread_cond_broadcast() call.
Your program
Your program has multiple problems in this area, among them:
Both threads access shared variables rem and count without synchronization, and some of the accesses are writes. The behavior of the whole program is therefore undefined. Among the common manifestations would be that the threads do not observe each other's updates to those variables, though it's also possible that things seem to work as expected. Or anything else.
Both threads call pthread_cond_wait() without holding the mutex locked. The behavior of the whole program is therefore undefined. "Undefined" means "undefined", but it is plausible that the UB would manifest as, for example, one or both threads failing to return from their wait after the CV is signaled.
Neither thread employs the standard pattern for CV usage. There is no clear associated condition for either one, and the threads definitely don't test one. That leaves an implied condition of "this CV has been signaled", but that is unsafe because it cannot be tested before waiting. In particular, it leaves open this possible chain of events:
The main thread blocks waiting on cond1.
The second thread signals cond1.
The main thread runs all the way at least through signaling cond2 before the second thread proceeds to waiting on cond2.
Once (3) occurs, the program cannot avoid deadlock. The main thread breaks from the loop and tries to join the second thread, meanwhile the second thread reaches its pthread_cond_wait() call and blocks awaiting a signal that will never arrive.
That chain of events can happen even if the issues called out in the previous points is corrected, and it could manifest exactly the observable behavior you report.

dispatch_queue_set_specific vs. getting the current queue

I am trying to get my head around the difference and usage between these 2:
static void *myFirstQueue = "firstThread";
dispatch_queue_t firstQueue = dispatch_queue_create("com.year.new.happy", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_set_specific(firstQueue, myFirstQueue, (void*) myFirstQueue, NULL);
Question #1
What is the difference between this:
dispatch_sync(firstQueue, ^{
if(dispatch_get_specific(myFirstQueue))
{
//do something here
}
});
and the following:
dispatch_sync(firstQueue, ^{
if(firstQueue == dispatch_get_current_queue())
{
//do something here
}
});
?
Question #2:
Instead of using the above (void*) myFirstQueue in
dispatch_queue_set_specific(firstQueue, myFirstQueue, (void*) myFirstQueue, NULL);
Can we use a static int * myFirstQueue = 0; instead ?
My reasoning is based on the fact that:
dispatch_once_t is also 0 (is there any correlation here? By the way, I still don’t quite get why dispatch_once_t must be initialized to 0, although I have already read questions here on SO).
Question #3
Can you cite me an example of GCD Deadlock here?
Question #4
This might be a little too much to ask; I will ask anyway, in case someone happens to know this off on top of the head. If not, it would be OK to leave this part unanswered.
I haven’t tried this, because I really don’t know how. But my concept is this:
Is there anyway we can "place a handle" in some queue that enables us to still withhold a handle on it and thus be able to detect when a deadlock occurs after the queue is spun off; and when there is, and since we got a handle of queue we previously set, we could somehow do something to unlock the deadlock?
Again, if this is too much to answer, either that or if my reasoning is completely undoable / off here (in Question #4), feel free to leave this part unanswered.
Happy New Year.
#san.t
With static void *myFirstQueue = 0;
We do this:
dispatch_queue_set_specific(firstQueue, &myFirstQueue, &myFirstQueue, NULL);
Totally understandable.
But if we do:
static void *myFirstQueue = 1;
//or any other number other than 0, it would be OK to revert back to the following?
dispatch_queue_set_specific(firstQueue, myFirstQueue, (void*) myFirstQueue, NULL);
Regarding dispatch_once_t:
Could you elaborate more on this:
Why must dispatch_once_t first be 0, and how and why would it need to act as a boolean at later stage? Does this have to do with memory / safety or the fact that the previous memory address was occupied by other objects that were not 0 (nil)?
As for Question #3:
Sorry, as I might not be completely clear: I didn’t mean I am experiencing a deadlock. I meant whether or not someone can show me a scenario in code with GCD that leads to a deadlock.
Lastly:
Hopefully you could answer Question #4. If not, as previously mentioned, it’s OK.
First, I really don't think you meant to make that queue concurrent. dispatch_sync()ing to a concurrent queue is not really going to accomplish much of anything (concurrent queues do not guarantee ordering between blocks running on them). So, the rest of this answer assumes you meant to have a serial queue there. Also, I'm going to answer this in general terms, rather than your specific questions; hopefully that's ok :)
There are two fundamental issues with using dispatch_get_current_queue() this way. One very broad one that can be summarized as "recursive locking is a bad idea", and one dispatch-specific one that can be summarized as "you can and often will have more than one current queue".
Problem #1: Recursive locking is a bad idea
The usual purpose of a private serial queue is to protect an invariant of your code ("invariant" being "something that must be true"). For example, if you're using a queue to guard access to a property so that it's thread-safe, then the invariant is "this property does not have an invalid value" (for example: if the property is a struct, then half the struct could have a new value and half could have the old value, if it was being set from two threads at once. A serial queue forces one thread or the other to finish setting the whole struct before the other can start).
We can infer that for this to make sense, the invariant has to hold when beginning execution of a block on the serial queue (otherwise, it clearly wasn't protected). Once the block has begun executing, it can break the invariant (say, set the property) without fear of messing up any other threads as long as the invariant holds again by the time it returns (in this example, the property has to be fully set).
Summarizing just to make sure you're still following: at the beginning and end of each block on a serial queue, the invariant that queue is protecting must hold. In the middle of each block, it may be broken.
If, inside the block, you call something which tries to use the thing protected by the queue, then you've changed this simple rule to a much much more complicated one: instead of "at the beginning and end of each block" it's "at the beginning, end, and at any point where that block calls something outside of itself". In other words, instead of thinking about your thread-safety at the block level, you now have to examine every individual line of each block.
What does this have to do with dispatch_get_current_queue()? The only reason to use dispatch_get_current_queue() here is to check "are we already on this queue?", and if you're already on the current queue then you're already in the scary situation above! So don't do that. Use private queues to protect things, and don't call out to other code from inside them. You should already know the answer to "am I on this queue?" and it should be "no".
This is the biggest reason dispatch_get_current_queue() was deprecated: to stop people from trying to simulate recursive locking (what I've described above) with it.
Problem #2: You can have more than one current queue!
Consider this code:
dispatch_async(queueA, ^{
dispatch_sync(queueB, ^{
//what is the current queue here?
});
});
Clearly queueB is current, but we're also still on queueA! dispatch_sync causes the work on queueA to wait for the completion of work on queueB, so they're both effectively "current".
This means that this code will deadlock:
dispatch_async(queueA, ^{
dispatch_sync(queueB, ^{
dispatch_sync(queueA, ^{});
});
});
You can also have multiple current queues by using target queues:
dispatch_set_target_queue(queueB, queueA);
dispatch_sync(queueB, ^{
dispatch_sync(queueA, ^{ /* deadlock! */ });
});
What would really be needed here is something like a hypothetical "dispatch_queue_is_synchronous_with_queue(queueA, queueB)", but since that would only be useful for implementing recursive locking, and I've already described how that's a bad idea... it's unlikely to be added.
Note that if you only use dispatch_async(), then you're immune to deadlocks. Sadly, you're not at all immune to race conditions.
Question 1:
The two code snip does the same thing, that is "does some work" when the block is indeed running in firstQueue. However they are using different ways to detect it is running on firstQueue, the first one sets a non NULL context ((void*)myFirstQueue) with a specific key (myFirstQueue) and later checks that the context is indeed non NULL; the second checks by using the now deprecated function dispatch_get_current_queue. The first method is preferred. But then it seems unnecessary to me, dispatch_sync guarantees the block to run in firstQueue already.
Question 2:
just using static int * myFirstQueue = 0; is not ok, this way, myFirstQueue is a NULL pointer and dispatch_queue_set_specific(firstQueue, key, context, NULL); requires non NULL key & context to work. However it will work with minor changes like this:
static void *myFirstQueue = 0;
dispatch_queue_t firstQueue = dispatch_queue_create("com.year.new.happy", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_set_specific(firstQueue, &myFirstQueue, &myFirstQueue, NULL);
this would use the address of myFirstQueue variable as key and context.
If we do:
static void *myFirstQueue = 1;
//or any other number other than 0, it would be OK to revert back to the following?
dispatch_queue_set_specific(firstQueue, myFirstQueue, (void*) myFirstQueue, NULL);
I guess it will be fine, as both myFirstQueue pointer won't be dereferenced provided the last destructor parameter is NULL
dispatch_once_t is also 0 has nothing to do with this. It is 0 at first and after it's dispatched once, it's value will change to non zero, essentially acting as a boolean.
Here are extracts from once.h, you can see that dispatch_once_t is actually a long and that Apple's implementation detail requires it to be initially 0, probably because static & global variables defaults to zero. And you can see that there is a line:
if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
essentially checking the once predicate is still zero before calling the dispatch_once function. It is not related to memory safety.
/*!
* #typedef dispatch_once_t
*
* #abstract
* A predicate for use with dispatch_once(). It must be initialized to zero.
* Note: static and global variables default to zero.
*/
typedef long dispatch_once_t;
/*!
* #function dispatch_once
*
* #abstract
* Execute a block once and only once.
*
* #param predicate
* A pointer to a dispatch_once_t that is used to test whether the block has
* completed or not.
*
* #param block
* The block to execute once.
*
* #discussion
* Always call dispatch_once() before using or testing any variables that are
* initialized by the block.
*/
#ifdef __BLOCKS__
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
_dispatch_once(dispatch_once_t *predicate, dispatch_block_t block)
{
if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
dispatch_once(predicate, block);
}
}
#undef dispatch_once
#define dispatch_once _dispatch_once
#endif
Question 3:
assuming myQueue is serial, concurrent queues are ok.
dispatch_async(myQueue, ^{
dispatch_sync(myQueue, ^{
NSLog(#"This would be a deadlock");
});
});
Question 4: Not sure about it.

One thread showing interest in another thread (consumer / producer)

I would like to have to possibility to make thread (consumer) express interest in when another thread (producer) makes something. But not all the time.
Basically I want to make a one-shot consumer. Ideally the producer through would go merrily about its business until one (or many) consumers signal that they want something, in which case the producer would push some data into a variable and signal that it has done so. The consumer will wait until the variable has become filled.
It must also be so that the one-shot consumer can decide that it has waited too long and abandon the wait (a la pthread_cond_timedwait)
I've been reading many articles and SO questions about different ways to synchronize threads. Currently I'm leaning towards a condition variable approach.
I would like to know if this is a good way to go about it (being a novice at thread programming I probably have quite a few bugs in there), or if it perhaps would be better to (ab)use semaphores for this situation? Or something else entirely? Just an atomic assign to a pointer variable if available? I currently don't see how these would work safely, probably because I'm trying to stay on the safe side, this application is supposed to run for months, without locking up. Can I do without the mutexes in the producer? i.e.: just signal a condition variable?
My current code looks like this:
consumer {
pthread_mutex_lock(m);
pred = true; /* signal interest */
while (pred) {
/* wait a bit and hopefully get an answer before timing out */
pthread_cond_timedwait(c, m, t);
/* it is possible that the producer never produces anything, in which
case the pred will stay true, we must "designal" interest here,
unfortunately the also means that a spurious wake could make us miss
a good answer, no? How to combat this? */
pred = false;
}
/* if we got here that means either an answer is available or we timed out */
//... (do things with answer if not timed out, otherwise assign default answer)
pthread_mutex_unlock(m);
}
/* this thread is always producing, but it doesn't always have listeners */
producer {
pthread_mutex_lock(m);
/* if we have a listener */
if (pred) {
buffer = "work!";
pred = false;
pthread_cond_signal(c);
}
pthread_mutex_unlock(m);
}
NOTE: I'm on a modern linux and can make use of platform-specific functionality if necessary
NOTE2: I used the seemingly global variables m, c, and t. But these would be different for every consumer.
High-level recap
I want a thread to be able to register for an event, wait for it for a specified time and then carry on. Ideally it should be possible for more than one thread to register at the same time and all threads should get the same events (all events that came in the timespan).
What you want is something similar to a std::future in c++ (doc). A consumer requests a task to be performed by a producer using a specific function. That function creates a struct called future (or promise), holding a mutex, a condition variable associated with the task as well as a void pointer for the result, and returns it to the caller. It also put that struct, the task id, and the parameters (if any) in a work queue handled by the producer.
struct future_s {
pthread_mutex_t m;
pthread_cond_t c;
int flag;
void *result;
};
// basic task outline
struct task_s {
struct future_s result;
int taskid;
};
// specific "mytask" task
struct mytask_s {
struct future_s result;
int taskid;
int p1;
float p2;
};
future_s *do_mytask(int p1, float p2){
// allocate task data
struct mytask_s * t = alloc_task(sizeof(struct mytask_s));
t->p1 = p1;
t->p2 = p2;
t->taskid = MYTASK_ID;
task_queue_add(t);
return (struct future_s *)t;
}
Then the producer pull the task out of the queue, process it, and once terminated, put the result in the future and trigger the variable.
The consumer may wait for the future or do something else.
For a cancellable futures, include a flag in the struct to indicate that the task is cancelled. The future is then either:
delivered, the consumer is the owner and must deallocate it
cancelled, the producer remains the owner and disposes of it.
The producer must therefore check that the future has not been cancelled before triggering the condition variable.
For a "shared" future, the flag turns into a number of subscribers. If the number is above zero, the order must be delivered. The consumer owning the result is left to be decided between all consumers (First come first served? Is the result passed along to all consumers?).
Any access to the future struct must be mutexed (which works well with the condition variable).
Regarding the queues, they may be implemented using a linked list or an array (for versions with limited capacity). Since the functions creating the futures may be called concurrently, they have to be protected with a lock, which is usually implemented with a mutex.

Why the child thread block in my code?

I was trying to test the pthread by using mutex:
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int global = 0;
void thread_set();
void thread_read();
int main(void){
pthread_t thread1, thread2;
int re_value1, re_value2;
int i;
for(i = 0; i < 5; i++){
re_value1 = pthread_create(&thread1,NULL, (void*)&thread_set,NULL);
re_value2 = pthread_create(&thread2,NULL,(void*)&thread_read,NULL);
}
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
/* sleep(2); */ // without it the 5 iteration couldn't finish
printf("exsiting\n");
exit(0);
}
void thread_set(){
pthread_mutex_lock(&mutex1);
printf("Setting data\t");
global = rand();
pthread_mutex_unlock(&mutex1);
}
void thread_read(){
int data;
pthread_mutex_lock(&mutex1);
data = global;
printf("the value is: %d\n",data);
pthread_mutex_unlock(&mutex1);
}
without the sleep(), the code won't finish the 5 iteration:
Setting data the value is: 1804289383
the value is: 1804289383
Setting data the value is: 846930886
exsiting
Setting data the value is: 1804289383
Setting data the value is: 846930886
the value is: 846930886
exsiting
It works only add the sleep() to the main thread, I think it should work without the sleep(), because the join() function wait for each child thread terminate
Any one can tell me why is it?
Your use of mutex objects looks fine, but this loop
for(i = 0; i < 5; i++) {
re_value1 = pthread_create(&thread1,NULL, (void*)&thread_set,NULL);
re_value2 = pthread_create(&thread2,NULL,(void*)&thread_read,NULL);
}
is asking for trouble as you are reusing the same thread instances thread1 and thread2 for each iteration of your loop. Internally this must be causing problems although I do not know exactly how it would manifest itself. You should really use a separate instance of thread object per thread you want to ensure reliable running. I do not know what would happen if you called pthread_create using an instance of a thread object that is already running but I suspect it is not a wise thing to do. I would suspect that at best it will block until the thread function has exited.
Also you are not cheking the return values from pthread_create() either which might be a good idea. In summary I would use a separate instance of thread objects, or add the pthread_join calls to the inside of your loop so that you are certain that the threads are finished running before the next call to pthread_create().
Finally the function signature for functions passed to pthread_create() are of the type
void* thread_function(void*);
and not
void thead_function()
like you have in your code.
You are creating 10 threads (5 iterations of two threads each), but only joining the last two you create (as mathematician1975 notes, you're re-using the thread handle variables, so the values from the last iteration are the only ones you have available to join on). Without the sleep(), it's quite possible that the scheduler has not started executing the first 8 threads before you hit exit(), which automatically terminates all threads, whether they have had a chance to run yet or not.

POSIX threading on ios

I've started experimenting with POSix threads using the ios platform. Coming fro using NSThread it's pretty daunting.
Basically in my sample app I have a big array filled with type mystruct. Every so often (very frequently) I want to perform a task with the contents of one of these structs in the background so I pass it to detachnewthread to kick things off.
I think I have the basics down but Id like to get a professional opinion before I attempt to move on to more complicated stuff.
Does what I have here seem "o.k" and could you point out anything missing that could cause problems? Can you spot any memory management issues etc....
struct mystruct
{
pthread thread;
int a;
long c;
}
void detachnewthread(mystruct *str)
{
// pthread_t thread;
if(str)
{
int rc;
// printf("In detachnewthread: creating thread %d\n", str->soundid);
rc = pthread_create(&str->thread, NULL, DoStuffWithMyStruct, (void *)str);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
//exit(-1);
}
}
//
/* Last thing that main() should do */
// pthread_exit(NULL);
}
void *DoStuffWithMyStruct(void *threadid)
{
mystruct *sptr;
dptr = (mystruct *)threadid;
// do stuff with data in my struct
pthread_detach(soundptr->thread);
}
One potential issue would be how the storage for the passed in structure mystruct is created. The lifetime of that variable is very critical to its usage in the thread. For example, if the caller of detachnewthread had that declared on the stack and then returned before the thread finished, it would be undefined behavior. Likewise, if it were dynamically allocated, then it is necessary to make sure it is not freed before the thread is finished.
In response to the comment/question: The necessity of some kind of mutex depends on the usage. For the sake of discussion, I will assume it is dynamically allocated. If the calling thread fills in the contents of the structure prior to creating the "child" thread and can guarantee that it will not be freed until after the child thread exits, and the subsequent access is read/only, then you would not need a mutex to protect it. I can imagine that type of scenario if the structure contains information that the child thread needs for completing its task.
If, however, more than one thread will be accessing the contents of the structure and one or more threads will be changing the data (writing to the structure), then you probably do need a mutex to protect it.
Try using Apple's Grand Central Dispatch (GCD) which will manage the threads for you. GCD provides the capability to dispatch work, via blocks, to various queues that are managed by the system. Some of the queue types are concurrent, serial, and of course the main queue where the UI runs. Based upon the CPU resources at hand, the system will manage the queues and necessary threads to get the work done. A simple example, which shows the how you can nest calls to different queues is like this:
__block MYClass *blockSelf=self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[blockSelf doSomeWork];
dispatch_async(dispatch_get_main_queue(), ^{
[blockSelf.textField setStringValue:#"Some work is done, updating UI"];
});
});
__block MyClass *blockSelf=self is used simply to avoid retain cycles associated with how blocks work.
Apple's docs:
http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
Mike Ash's Q&A blog post:
http://mikeash.com/pyblog/friday-qa-2009-08-28-intro-to-grand-central-dispatch-part-i-basics-and-dispatch-queues.html

Resources