[SOLVED] Please help me use condition variables in the lthread library!
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I have written a slightly complex application which uses pthreads and lthread to asynchronously update information for displaying graphics which represent the goings on of a complex system. One thread per graphic element-very much like sprites. It's not necessary to synchronize the display of these sprites, so that's not the question.
I'd resisted the (very good, helpful) suggestion of using a condition variable to signal the threads to wake. It's become unnecessary for them to busy-wait since, but I'd like to learn to use the lthread_cond_wait and lthread_cond_broadcast functions and simply cannot figure it out.
lthread uses pthreads and plays well with them. I can compile using or not using pthreads and have the same problem.
And may I be clear that I'm looking for suggestion about making lthread work, not for suggestions to use pthreads alone, or even to use another lightweight thread library.
I simply wish to learn the use of condition variables in lthread. I've learned how to use them in pthreads, thanks to the great book, "Linux Programming Interface_ A Linux and UNIX System Programming Handbook, The - Michael Kerrisk." So I'm confused. lthread seems to be as simple. Or simpler.
The following variable has global scope:
Code:
lthread_cond_t *makemore
the function with creates and launches the lthreads dynamically initializes the condition variable using this call:
Code:
lthread_cond_create(&makemore);
Then it creates and launches the threads.
The individual threads then do their thing and wait, calling:
Code:
lthread_cond_wait(makemore,0);
The second value passed is how many milliseconds to sleep- 0 indicating indefinitely, until signaled to awaken. They cooperatively multithread, so the lthread scheduler is supposed to wake 'em up, I think, when this function is called:
Code:
lthread_cond_broadcast(makemore);
instead of that, it crashes or hangs. If I broadcast from within an lthread running on a separate scheduler from the lthreads waiting on the signal, it all crashes. with segmentation fault. From an lthread running on the same scheduler, everydarnthing just stops.
I'm not sure what other information to include. Suggestions for further info to provide or how to make this work would be so welcomed.
by the by, I must apologize to sundialsvcs for not taking this suggestion I quote here right away. This suggestion IS simpler. I just don't know how to get it working.
Quote:
Condition-variables are a good way to do this sort of thing in traditional threading systems. They are, in effect, "the little red flag on the mailbox," which indicates to the driver that the box should be opened because it might have a piece of outbound mail in it.
I wrote a short program distilling the puzzle that's been bugging me. It creates a pthread, which then creates and launches twenty lightweight threads using the lthread library.
I almost forgot, this is not a standard library and may not be that commonly in use, regardless how great it is. I found it here, downloaded the source and compiled, simply following the directions and it worked first time. I'm using a 64bit amd/intel computer with Ubuntu 15.10.
If the environment variable usecondsig is not 0, it will create either another lthread, OR a pthread - either of which broadcast an lthread_cond_t signal to tell the twenty theads to report their numbers ten times, then to quit.
If the environment variable pthreadsig is set to nonzero, a pthread will be used to broadcast the lthread_cond_t variable, otherwise an lthread will to the job
However, if lthread_cond_broadcast() is called from the pthread, it all crashes with a segfault.
I hope that's at least sort of clear. There are all kinds of reasons for doing this, and I'm not interested in why or why not, just how.
I mentioned the use of two environment variables: pthreadsig to choose what calls lthread_cond_broadcast(), and usecondsig to control whether to call it at all.
If you set usecondsig to zero, you can watch the behavior of the threads indefinitely since they won't be signaled.
A third environment variable is useful: randquit -set to non-zero and the lthreads will have a one in fifty chance of quitting every time they loop, so if you don't send a signal with a condition variable to tell them to quit, they'll quit eventually anyhow
The threads print out what they're doing so you can see their behavior in the terminal window, or from a real terminal, you invoke thxc from.
OK, this is my first attempt to post a serious question plus a compilable code example, and I hope I've made it clear and workable. I've compiled and tested this code just as I've posted it, using the same command line, emm, command to gcc to compile it as I posted. In fact I just cut and paste, so I'm very sure it'll work.
Tho the code isn't my actual project-which is too too long for here-it was really good for me to try to distill the thread handling stuff here. It's definitely helped me think and I think maybe you must call the lthread_cond_create() function from the same pthread as the scheduler is running on, which really bugs me so I hope I'm wrong. Other than that, this lthread stuff is hunky-dory by me!
#include <lthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
/*each lthread which will wait for that signal, and report **
** its serial number is represented in, and passed a copy ***
** of this struct. I hope it's self explanatory enough***/
typedef struct whatiam{
int serialnumber;
lthread_cond_t *mysignal;
char **myinstructions;
lthread_t *my_lightweight_thread_variable;
} me_t;
lthread_t *an_lthread_signals;
pthread_t a_pthread_signals;
/*these globals are set through environment variables***
** with the exact same spelling.*/
int pthreadsig, //1=broadcast cond variable from in a pthread
randquit, //1=lthreads will quit eventually at random
usecondsig; //0=don't broadcast condition variable
lthread_cond_t *serialnumber_lthread_wakeup_signal;
char *set_to_quit_or_not = "don't quit";
const int yes = 1,
no = 0;
//returns from 0 to maximum
// generate random numbers for sleep and wait functions
int
intrand( int maximum ){
if (!maximum)return 0;
unsigned long intermediate = rand();
intermediate *= (abs(maximum)+1);
intermediate /= INT_MAX;
return (int)intermediate;
}
void lthread_thread(void *who_I_am){
me_t *myself = who_I_am;
for(;;){
int report = lthread_cond_wait(myself->mysignal, intrand(20)+1);
//nonblocking sleep from 1 to 21 millisecond for signal then resumes
if (!report){//return of zero means woke from the signal
if(!strcmp("quit", *myself->myinstructions)){
printf("serial number:\t %i\tSIGNALLED TO QUIT!\n\n", myself->serialnumber);
break;
}else{
printf("serial number:\t %i\treporting!\t", myself->serialnumber);
}
}else if (report==-2){ // if the lthread_cond_wait times out it reports -2
printf("timed out!!!:\t %i\ttimed out!!!\t", myself->serialnumber);
}else{ // other return values not defined in the API
printf("\n\nabnormal return from lthread_cond_wait\n\n");
break;
}
if((randquit)&&(intrand(50)>49)){ //random quitting helpful if no cond. signal is broadcast
printf("\nserial number:\t %i\tQUITTING AT RANDOM!!!\n", myself->serialnumber);
break;
}
}
return;
}
void *pthread_signals_controls_lthreads(void *unused){
// the loop below should make the threads report their numbers
for(int i = 0; i < 10; i++){
usleep(50000);// nonblocking sleep for 20ms (20000us)
printf("\n\npthread is now calling lthread_cond_broadcast() to signal lthreads to report\n\n");
lthread_cond_broadcast(serialnumber_lthread_wakeup_signal);
}
// now they should all report quitting and serial numbers
set_to_quit_or_not = "quit";
printf("\n\npthread is now calling lthread_cond_broadcast() to signal lthreads to quit\n\n");
lthread_cond_broadcast(serialnumber_lthread_wakeup_signal);
printf("\n\nlthread_cond_broadcast() has returned now the lthreads should be quitting\n\n");
return NULL;
}
void lthread_signals_controls_lthreads(void *unused){
for(int i = 0; i < 10; i++){
printf("\n\nlthread is now calling lthread_cond_broadcast() to signal lthreads to report\n\n");
lthread_cond_broadcast(serialnumber_lthread_wakeup_signal);
lthread_sleep(50);// nonblocking sleep for 20 ms
}
// now they should all report quitting and serial numbers
set_to_quit_or_not = "quit";
printf("\n\nlthread is now calling lthread_cond_broadcast() to signal lthreads to quit\n\n");
lthread_cond_broadcast(serialnumber_lthread_wakeup_signal);
printf("\n\nlthread_cond_broadcast() has returned now the lthreads should be quitting\n\n");
return NULL;
}
void *pthread_launches_lthreads(void *unused){
lthread_cond_create(&serialnumber_lthread_wakeup_signal);
const int number_of_lthreads = 20;
set_to_quit_or_not = "don't quit";
me_t *array_of_lthreads = malloc(number_of_lthreads*sizeof(me_t));
for (int i = 0; i < number_of_lthreads; i++){
array_of_lthreads[i].serialnumber = i;
array_of_lthreads[i].mysignal = serialnumber_lthread_wakeup_signal;
array_of_lthreads[i].myinstructions = &set_to_quit_or_not;
/* The thread is represented by this:
array_of_lthreads[i].my_lightweight_thread_variable
the function serving as the start of the thread is
lthread_thread the data passed to the thread is a
structure: &array_of_lthreads[i]
alternately array_of_lthreads+i could have been used*/
lthread_create(&array_of_lthreads[i].my_lightweight_thread_variable, lthread_thread, &array_of_lthreads[i]);
}
if((usecondsig)&&(pthreadsig)){
pthread_create(&a_pthread_signals, NULL, pthread_signals_controls_lthreads, NULL);
}else if(usecondsig){
lthread_create(&an_lthread_signals, lthread_signals_controls_lthreads, NULL);
}
/*the next function starts the lthread scheduler which will
schedule all the lthreads created using lthread_create within
this pthread. You can also create lthreads and run them from
main();
once the lthread scheduler is running the pthread is effectively paused until all the lthreads exit*/
lthread_run();
printf("\n\nthe lthread scheduler has run out of lthreads to schedule\n\n");
return NULL;
}
int main(int argc, char **argv){
char *envvar = getenv("pthreadsig");
pthreadsig = envvar ? atoi(envvar): yes;
envvar = getenv("randquit");
randquit = envvar ? atoi(envvar): no;
envvar = getenv("usecondsig");
usecondsig = envvar ? atoi(envvar): no;
pthread_t create_launch_lthreads;
pthread_create(&create_launch_lthreads, NULL, pthread_launches_lthreads, NULL);
pthread_join(create_launch_lthreads, NULL);
return 0;
}
Last edited by DeeDeeK; 01-24-2017 at 10:26 PM.
Reason: adding url for lthread source, documentation, so you can download and compile
Resumes lthread inside a pthread to run expensive computations or make a blocking call like gethostbyname(). This call must be followed by lthread_compute_end() after the computation and/or blocking calls statements have been made, to resume the lthread in its original lthread scheduler. No lthread_* calls can be made during the 2 calls.
I.e., while the lthread is running in a different pthread, you can't use any lthread functions.
I had overlooked that fact, that you can't invoke lthread calls from within the pthread which lthread_compute_begin() and lthread_compute_end()! Very obvious but I wasn't looking there.
I was just imagining that a condition variable must be global-and I guess by declaration it might be, but [I]initialization[/I] is done within a pthread, or the main body of the program-which is a kind of thread itself I think.
Dang! I tried everydarnthing I could imagine. Read the instructions-my mother always told me that.
I think I've got an idea which will work for my purposes. To try it out, first I'm going to try modifying the code I posted earlier,to create two sets of otherwise identical lthreads-say serial numbers 0-19 from one pthread and 20-39 on a second pthread, and I'll have an lthread_signals_controls_lthreads() for each set, and inside the lthread_signals_controls_lthreads() I'll have it wait on a pthread_cond_t, sitting between lthread_compute_begin() and lthread_compute_end().
I'm psyched. Thanx. I'll post again, to give the results-if it works how I'm guessing, it'll be question answered.
I had overlooked that fact, that you can't invoke lthread calls from within the pthread which lthread_compute_begin() and lthread_compute_end()! Very obvious but I wasn't looking there.
Well, it's not that obvious. In fact, if it weren't for the fact that the github repo seems to be unmaintained, I would suggest reporting this as a documentation bug. You shouldn't have to go hunting to ferret out this kind of fundamental restriction from the docs.
Quote:
I was just imagining that a condition variable must be global-and I guess by declaration it might be, but initialization is done within a pthread, or the main body of the program-which is a kind of thread itself I think.
It's not just about the initialization, it's likely that the lthread functions simply don't perform the locking needed to provide thread safety.
Orig. posted by ntubski
Well, it's not that obvious. In fact, if it weren't for the fact that the github repo seems to be unmaintained, I would suggest reporting this as a documentation bug. You shouldn't have to go hunting to ferret out this kind of fundamental restriction from the docs.
Do you know of any currently maintained lightweight thread libraries which have lthread's sort of functionality? I really really really like the functions lthread_compute_begin() and lthread_compute_end()!
Being able to launch an lthread, then have it become nonblocking is so useful. I'd hate for this library to fade away without something similar to replace it. I'd like to learn enough to write my own, or be able to maintain it.
I was looking an GNU Pth, the GNU Portable Threads library, but it looks like it's been maybe ten years since it's been active?
I'm working on the code to synchronize several lthread schedulers using pthread condition variable signalling. I'll post it when it's working. I've had over 400 threads, each one managing a single sprite, animating it, doing collisions, and interacting with the others with an adapted version of Newtonian gravity, running quickly, all on one core. It is so easy to switch from using lthreads to pthreads, that I wrote it to use pthreads instead of lthreads if an evironmenta variable is set, and pthreads cannot begin to keep up. At 500 threads, pthreads is bogging down and lthreads is still reasonably quick. The difference is amazing.
Do you know of any currently maintained lightweight thread libraries which have lthread's sort of functionality? I really really really like the functions lthread_compute_begin() and lthread_compute_end()!
Being able to launch an lthread, then have it become nonblocking is so useful. I'd hate for this library to fade away without something similar to replace it. I'd like to learn enough to write my own, or be able to maintain it.
I don't know of any, but if you're really willing to maintain this one, you can try fixing things in your own fork and/or contact the author to get write access.
I think that there might be a fundamental mis-conception here about how lthreads ("lightweight threads")fundamentally differ from pthreads ("POSIX, i.e. operating-system, threads").
To quote the documentation:
Quote:
Originally Posted by http://lthread.readthedocs.io/en/latest/intro.html:
lthreads are created in userspace and don’t require kernel intervention, they are light weight and ideal for socket programming. Each lthread have separate stack, and the stack is madvise(2)-ed to save space, allowing you to create thousands(tested with a million lthreads) of coroutines and maintain a low memory footprint. The scheduler is hidden from the user and is created automagically in each pthread ...
Lightweight threads, of whatever flavor, are cooperatively multi-tasked. This is not an operating-system facility: it is a cooperative activity that requires a certain discipline from every lightweight-thread. The first diagram in the cited documentation shows how the lthread library wraps-up collections of lightweight-threads within an operating-system-dispatched pthread.
With such an arrangement I suggest that it should be "all or nothing." Either "do everything the lthread way, or don't do anything." This will be a lightweight and fast implementation of "co-routines" but it probably will not brook much interference from something else that is trying to use "pthreads" at the same time. Because it is not built by nor enforced by the operating system kernel, it is by architecture "somewhat fragile."
For instance, I would not attempt to use "operating-system 'condition variables'" atall in connection to such a library. I would not expect it to work. I would expect it to break things. If you cause the underlying pthread to be suspended, it cannot cooperatively dispatch any lightweight tasks.
Last edited by sundialsvcs; 01-29-2017 at 09:45 AM.
For instance, I would not attempt to use "operating-system 'condition variables'" atall in connection to such a library. I would not expect it to work. I would expect it to break things. If you cause the underlying pthread to be suspended, it cannot cooperatively dispatch any lightweight tasks.
I've found that I can co-ordinate two sets of lightweight threads, each running on a different scheduler-and each scheduler is of course running in its own pthread. For each group of worker lthreads running on a scheduler, there is one more which uses the facility for not blocking other lthreads while making blocking calls, which the Lthreads library provides. So that one thread sits, waiting for that pthread_cond_t broadcast, and when it receives that broadcast, then makes an lthread_cond_broadcast call, to synchronize all the lthreads sharing its scheduler. No violation and no confusion.
It's important that lthreads be in user space and that it be separate but closely interoperating with pthreads, for this scheme to work. The trick is, in the middle of a lightweight thread, you can have nonblocking code-running on ANOTHER pthread-which you don't have to think about. Just call lthread_compute_begin() then write your otherwise blocking code, and after that blocking code is over, call lthread_compute_end(). SO, in the lthread which I have signal the other lthreads from, it actually blocks on that pthread_cond_t, but doesn't disrupt anything else.
So I can have several hundred or thousand lthreads running on one core, that many running on another - taking advantage of core associativity to keep a pthread running on a particular core - and an equal number of light weight threads running on another core, and then use pthread_cond_broadcast or pthread_cond_signal to let the two schedulers be in sync. I have it working already. It's hard to describe but wasn't hard to code. I should say coupled-not synced. My example makes no use of mutexs or semaphores, just kind of wings it.
I enjoy learning about abstract principles, coding conventions, and styles, but when it comes right down to it, I make my decisions based on the particulars of any new library or reimplementation of a library I'm already familiar with. I've been told so many times that one thing or another won't work on principle, but discovered that in fact it will.
here's a sloppy little example I wrote just to test the concept. I'm expanding on it. My idea is eventually to use on computer to display and use sockets to hook up several others, all running thousands of threads, to create some complex systems for, duh, complex systems studies. I want to be able to visualize the behavior, sort of an extreme version of the game of life, except with real complexity. For now I'm just using a little physics and silly graphics, and having fun with it. This isn't about tightly synchronized, highly coupled threads, and I think if you relax your concepts of what threads must and must not be, or what they can and can't be used for, you'll maybe see where I'm going with it.
For right now, it's more than fast enough for videogaming, at least 2d, with lots of animation and physics. My older test-of-concept where I had just one lthread scheduler is bouncing several hundred animated sprites around like it was nothing. And it's embarrassingly simple to switch from a lthreads to a pthreads paradigm. I just use a single environment variable as a flag.
But this example isn't of the sprites. It's fun to play with the timing and see the behavior changing.
I am using GCC 5.1.1 but this should work on earlier versions. I tend to use a cookie cutter to make my make files and I just cut and pasted. Probably no need for gnu11 or fms-extensions or -g -pg. I include two different gcc invocations. Both worked for me just fine to compile.
... if you're really willing to maintain this one, you can try fixing things in your own fork and/or contact the author to get write access.
I am SO INTERESTED! A marvelous idea! It hadn't entered my mind but I really love this library, and feel that there is even more that could be done with it...But I am worried I might now be expert enough yet in C programming to do this.
I noticed that, obviously since I compiled it myself from source, I have the source code! Duh, yeah, but I am going to print it out on real paper so's I can mark it up by hand 'n stuff, and I am going to darn well learn enough that I could actually do just what you are suggesting!
I'm a little worried that the copyright holder won't be amenable, but then maybe he would. To have an active community maintaining it-even a community of two, himself and me for example-must carry some sort of hacker street-cred, don't you think?
What I would like to add are channels, bidirectional message passing channels between threads. Even between threads on different schedulers. If a thread can very simply pass a message to another thread, then that model could be abstracted and extended to use other means - even IPC, like sockets! This would make the kind of cross-core pthread/lthread synchronization attempted in the code I posted the other day irrelevant.
In my larger project, a specific instance of one of several structs holds data as well as functions which operate on that data, including its own thread. Sort of like a thread and sort of like an object. They don't have to be that way-I simply want it to be that way. It helps me to think of the problem that way.
These objects will represent actors in a complex system-like cellular automata but they aren't static, they move around and interact by contact or at a distance, as tho' through gravity or chemical signaling. Like bacteria or gravitating bodies do in the real world.
I was reading about the D language, which has messaging between threads, but I think I'd prefer to implement it myself, so I can control the details. After all, I started learning C because I'm creative and enjoy doing the work and getting my hands dirty.
Last edited by DeeDeeK; 01-31-2017 at 06:25 PM.
Reason: too long. cutting it shorter
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.