Thread: Is it possible to set/get user data to HANDLE/pthread_t?

  1. #1
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733

    Is it possible to set/get user data to HANDLE/pthread_t?

    Every attempt to find the information on the net just keeps coming up with irrelevant results around pthread_create() instead of say pthread_get_ud( thread )/pthread_set_ud( thread, ud ), I would prefer being able to get the final argument that gets passed onto the thread but no matter what I search it doesn't seem to be a thing despite it being such an obvious feature to need, just like windows, gc objects & others need.

    Edit: To be clear it's these sort of actions I'd like to do something similar to:
    Code:
    glfwSetWindowUserPointer( Win->handle, Win );
    ...
    void	onClosed( GLFWwindow *handle )
    {
    	pawWIN Win = glfwGetWindowUserPointer( handle );
    	if ( Win->onClosedCB )
    		Win->onClosedCB( Win );
    }
    Where all I have is the source api's reference to begin with

  2. #2
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    So far the only thing I've been able to come up with is this:
    Code:
    typedef struct _PAW_APPOBJ
    {
    	pawd perm;
    	pawjd pid;
    } PAW_APPOBJ, *pawPROC;
    
    #define BAD_PAW_APPOBJ { 0, -1 }
    
    typedef void* (*pawrun_cb)( void *ud );
    typedef struct _PAW_RUNOBJ
    {
    	pawd perm;
    	pawjd tid;
    	pawc _tag[bitsof(pawjd)];
    	void *ud;
    	pawrun_cb initCB;
    	pthread_t thread;
    } PAW_RUNOBJ, *pawEXEC;
    
    typedef const PAW_APPOBJ PAWPROC;
    typedef const PAW_APPOBJ PAWPROC;
    
    extern pawjd _pawthis_pid;
    PAW_APPOBJ paw_proc = BAD_PAW_APPOBJ;
    
    PAW_API pawjd	pawthis_tid() { return gettid(); }
    PAW_API pawjd	pawthis_pid() { return _pawthis_pid; }
    PAW_API pawapp	pawthis_app()
    {
    	paw_proc.pid = _pawthis_pid;
    	paw_proc.perm = 0700;
    	return &paw_proc;
    }
    PAW_API pawrun	pawthis_run()
    {
    	pawju addr = 0;
    	pawEXEC exec = NULL;
    	pawlsu lsu = BAD_pawlsu;
    	pthread_t thread = pthread_self();
    	pawc tag[bitsof(pawju)], ignored[bitsof(pawju)];
    	paw_set( tag, 0, bitsof(pawju) );
    	pthread_tag_np( ignored, tag );
    	paws2pawls( lsu->str, PAWLSU_MAX, tag, paws_len(tag) );
    	pawlsu2pawju( &addr, &lsu, PAW_BASE16X );
    	exec = (pawEXEC)addr;
    	if ( exec )
    		return exec;
    	pthread_tag( tag, ignored );
    	return NULL;
    }
    void* pawrun_initCB( void *ud )
    {
    	pawEXEC exec = UD;
    	pawlsu lsu = BAD_pawlsu;
    	pawc ignored_tag[bitsof(pawju)];
    	exec->tid = pawthis_tid();
    	pawju2pawlsu( &lsu, (pawvu)ud, PAW_BASE16X );
    	pawls2paws( exec->_tag, bitsof(pawju) - 1, lsu->str, lsu->len );
    	pthread_tag_np( exec->_tag, ignored_tag );
    	exec->initCB( exec->ud );
    	return ud;
    }
    PAW_API pawrun pawrun_create( void *attr, void *ud, pawrun_cb initCB )
    {
    	if ( _pawthis_pid >= 0 )
    	{
    		pawEXEC exec = pawgetUD( sizeof(PAW_RUNOBJ) );
    		if ( exec )
    		{
    			pawd did = 0;
    
    			exec->pid = _pawthis_pid;
    			exec->initCB = initCB;
    			exec->ud = ud;
    
    			did = pthread_create
    			(
    				&(exec->thread), NULL,
    				pawrun_initCB, exec
    			);
    			if ( did == 0 )
    				return thread;
    			pawdel(exec);
    		}
    	}
    	errno = ENODATA;
    	return NULL;
    }
    #endif
    Not ideal but all I got for now, still gotta investigate what I have available in windows, as for the _pawthis_pid, turns out getpid() changes value with gettid() so calling it is pointless in threads, instead I capture it at the start, I also use it to detect if the globals have yet to be initialised

  3. #3
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Realised there was a way to achieve what I originally wanted:
    Code:
    #else
    #include <sys/syscall.h>
    typedef struct _PAW_APPOBJ
    {
    	pawd perm;
    	pawjd pid;
    } PAW_APPOBJ, *pawPROC;
    
    #define BAD_PAW_APPOBJ { 0, -1 }
    
    typedef void* (*pawrun_cb)( void *ud );
    typedef struct _PAW_RUNOBJ
    {
    	pawjd tid;
    	void *ud;
    	pawrun_cb initCB;
    } PAW_RUNOBJ, *pawEXEC;
    
    #define BAD_PAW_RUNOBJ { -1, -1, NULL, NULL }
    
    typedef const PAW_APPOBJ PAWPROC;
    typedef const PAW_APPOBJ PAWPROC;
    
    extern pawjd _pawthis_pid;
    PAW_APPOBJ paw_proc = BAD_PAW_APPOBJ;
    
    PAW_API pawjd	pawthis_tid()
    {
    #ifdef _GNU_SOURCE
    	return gettid();
    #elif defined(__linux__)
    	return syscall(SYS_gettid);
    #elif defined(__FreeBSD__)
    	long tid;
    	thr_self(&tid);
    	return tid;
    #elif defined(__NetBSD__)
    	return _lwp_self();
    #elif defined(__OpenBSD__)
    	return getthrid();
    #else
    	return getpid();
    #endif
    }
    PAW_API pawjd	pawthis_pid() { return _pawthis_pid; }
    PAW_API pawapp	pawthis_app()
    {
    	paw_proc.pid = _pawthis_pid;
    	paw_proc.perm = 0700;
    	return &paw_proc;
    }
    PAW_API pawrun	pawthis_run() { return pawthis_tid(); }
    void* pawrun_initCB( void *ud )
    {
    	pawEXEC exec = ud;
    	PAW_RUNOBJ safe = *exec;
    	exec->tid = pawthis_tid();
    	safe.initCB( safe.ud );
    	return NULL;
    }
    PAW_API pawrun pawrun_create( void *attr, void *ud, pawrun_cb initCB )
    {
    	if ( _pawthis_pid >= 0 )
    	{
    		pawd did = -1;
    		pthread_t thread;
    		PAW_RUNOBJ exec = BAD_PAW_RUNOBJ;
    		exec->initCB = initCB;
    		exec->ud = ud;
    		if ( pthread_create( &thread, NULL, pawrun_initCB, &exec ) )
    			return BAD_pawrun;
    		while ( exec.tid < 0 )
    			paw_yield();
    		return exec.tid;
    	}
    	errno = ENODATA;
    	return BAD_pawrun;
    }
    #endif
    I wanted a portable interface for working with raw system stuff, I would then design my objects around that, open to ideas on how to return a user pointer from both win32 & linux, pthreads I can probably get away with just returning safe.ud but win32 returns a DWORD, without some means of associating a pointer with the thread I don't see a way to extract the pointer from the DWORD, best I can come up with would involve a hidden buffer & mutex, that also means I would have to ensure some way of termination on an exit() call which may be possible with what I have but I don't want to bank on it

  4. #4
    Registered User
    Join Date
    Sep 2020
    Posts
    425
    If somebody finds there way here looking for an answer to this problem, and is after a simple but non-portable solution. C11 standardard includes __Threadlocal

    Code:
    /////////////////////////////////////////////
    // Ideally put these in their own file,
    // and create a .h file for the functions
    ////////////////////////////////////////////
    static __thread char *tls_storage;
    
    
    char *get_thread_local(void) {
        return tls_storage;
    }
    
    
    char *set_thread_local(char *new_val) {
        char *old_val = tls_storage;
        tls_storage = new_val;
        return old_val;
    }
    Part of the ethos of threads is that they are separate threads of program execution in a shared memory space (but with their own stack space). Any use of thread-local storage chips away at this, and I feel should be avoided if at all possible.

    Oh, this snippet may help, as it switches to the C11 standard's "__Thread_local" attribute.

    Code:
    /* gcc doesn't know _Thread_local from C11 yet */
    #ifdef __GNUC__
    # define thread_local __thread
    #elif __STDC_VERSION__ >= 201112L
    # define thread_local _Thread_local
    #else
    # error Don't know how to define thread_local
    #endif
    Last edited by hamster_nz; 10-10-2022 at 01:47 AM.

  5. #5
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Quote Originally Posted by hamster_nz View Post
    If somebody finds there way here looking for an answer to this problem, and is after a simple but non-portable solution. C11 standardard includes __Threadlocal

    Code:
    /////////////////////////////////////////////
    // Ideally put these in their own file,
    // and create a .h file for the functions
    ////////////////////////////////////////////
    static __thread char *tls_storage;
    
    
    char *get_thread_local(void) {
        return tls_storage;
    }
    
    
    char *set_thread_local(char *new_val) {
        char *old_val = tls_storage;
        tls_storage = new_val;
        return old_val;
    }
    Part of the ethos of threads is that they are separate threads of program execution in a shared memory space (but with their own stack space). Any use of thread-local storage chips away at this, and I feel should be avoided if at all possible.

    Oh, this snippet may help, as it switches to the C11 standard's "__Thread_local" attribute.

    Code:
    /* gcc doesn't know _Thread_local from C11 yet */
    #ifdef __GNUC__
    # define thread_local __thread
    #elif __STDC_VERSION__ >= 201112L
    # define thread_local _Thread_local
    #else
    # error Don't know how to define thread_local
    #endif
    I wish these forums had a like button for posts, this woulda gotten a big thumbs up from me, so far the tag & name options are the only means I can find for a quick pointer system, the slower alternatives are either buffers or environment variables

  6. #6
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    btw I didn't mean thread local, I mean a pointer created for the thread prior to the thread itself, it's contents are supposed to be outside the thread's local storage

  7. #7
    Registered User
    Join Date
    Sep 2020
    Posts
    425
    Quote Originally Posted by awsdert View Post
    btw I didn't mean thread local, I mean a pointer created for the thread prior to the thread itself, it's contents are supposed to be outside the thread's local storage
    My working example does exactly that... saving the vargp value for later use.

    Code:
    ////////////////////////////////////////////
    // Example of using it
    ////////////////////////////////////////////
    void thread_close(void) {
        printf("My thread local data is %s\n", get_thread_local());
    }
    
    
    void *myThreadFunc(void *vargp)
    {
        set_thread_local(vargp); // Save it away for later
        sleep(1);
        thread_close();
        return NULL;
    }
    
    
    ////////////////////////////////////////////
    // main() starting two threads
    ////////////////////////////////////////////
    int main()
    {
        pthread_t thread_id1, thread_id2;
        printf("Before Thread\n");
        pthread_create(&thread_id1, NULL, myThreadFunc, "Thread1 data");
        pthread_create(&thread_id2, NULL, myThreadFunc, "Different data for Thread2");
        pthread_join(thread_id1, NULL);
        pthread_join(thread_id2, NULL);
        printf("After Thread\n");
        exit(0);
    }
    Code:
    $ /tmp/a
    Before Thread
    My thread local data is Different data for Thread2
    My thread local data is Thread1 data
    After Thread

  8. #8
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Quote Originally Posted by hamster_nz View Post
    My working example does exactly that... saving the vargp value for later use.

    Code:
    ////////////////////////////////////////////
    // Example of using it
    ////////////////////////////////////////////
    void thread_close(void) {
        printf("My thread local data is %s\n", get_thread_local());
    }
    
    
    void *myThreadFunc(void *vargp)
    {
        set_thread_local(vargp); // Save it away for later
        sleep(1);
        thread_close();
        return NULL;
    }
    
    
    ////////////////////////////////////////////
    // main() starting two threads
    ////////////////////////////////////////////
    int main()
    {
        pthread_t thread_id1, thread_id2;
        printf("Before Thread\n");
        pthread_create(&thread_id1, NULL, myThreadFunc, "Thread1 data");
        pthread_create(&thread_id2, NULL, myThreadFunc, "Different data for Thread2");
        pthread_join(thread_id1, NULL);
        pthread_join(thread_id2, NULL);
        printf("After Thread\n");
        exit(0);
    }
    Code:
    $ /tmp/a
    Before Thread
    My thread local data is Different data for Thread2
    My thread local data is Thread1 data
    After Thread
    My misunderstanding then

  9. #9
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Just to expand a bit on hamster_nz's suggestion, after learning of that extension I went and did a bit of research, for now I have this:
    Code:
    /* Thanks to hamster_nz for identifying this to me :)
     * https://cboard.cprogramming.com/c-programming/181257-possible-set-get-user-data-handle-pthread_t.html
     * */
    #ifdef __GNUC__
    #define paw_thread_local __thread
    #elif defined( _MSC )
    #define paw_thread_local _declspec(thread)
    #elseif PAW_STDC >= 202300L
    #define paw_thread_local thread_local
    #else /*if PAW_STDC >= 201113L*/
    #define paw_thread_local _Thread_local
    #endif
    
    thread_local pawRUN _pawRUN = NULL;
    It's actually supposed to have a #ifdef guard for paw_thread_local but I'll deal with that later, as for the fallback I decided that I won't both with a buffer for now and instead just rely on the environment variables to extract the pointer, here's a snippet from my current code, haven't tested it but I know the stringify/numerify functions work just fine so in theory no issue:
    Code:
    void* pawRUN_initCB( void *ud )
    {
    	pawRUN Run = ud;
    	_pawRUN = Run;
    	Run->tid = pawthis_tid();
    	if ( Run->initCB )
    	{
    		pawsu *key = &(Run->env_key), *val = &(Run->env_val);
    		pawju2pawsu( key, Run->tid, PAWS_BASE10 );
    		pawju2pawsu( val, (pawvu)Run, PAWS_BASE16X );
    		paw_ins( key->txt, key->len, PAWRUN_PFX, sizeof(PAWRUN_PFX) - 1 );
    		setenv( key->txt, val->txt, 1 );
    		pawgc_decl_ud( Run->id, Run );
    		pawgc_decl_voidCB( Run->id, pawRUN_voidCB );
    		Run->initCB( Run->id, Run->ud );
    		unsetenv( key->txt );
    	}
    	return NULL;
    }
    static pthread_t pawrun_pthread( pawrun run )
    {
    	paws txt = NULL;
    	pawsu key = BAD_pawsu, val = BAD_pawsu;
    	pawju addr = 0;
    	pawRUN Run = NULL;
    	pawju2pawsu( &key, run, PAWS_BASE16X );
    	/* We get at least 48 remaining characters to work with so no fear of
    	 * buffer overruns */
    	paw_ins( key.txt, key.len, PAWRUN_PFX, sizeof(PAWRUN_PFX)-1 );
    	txt = getenv( key.txt );
    	paw_cpy( val.txt, txt, paws_len(txt) );
    	pawsu2pawju( &addr, &val, PAWS_BASE16X );
    	Run = (void*)addr;
    	return Run->thread;
    }
    Just on the off chance the environment decides to use the text pointers as is I attached them to the thread object directly, I just gotta get round to clearing the variable prior to deleting the object.

  10. #10
    Registered User
    Join Date
    Sep 2020
    Posts
    425
    I just want to show you why people here aren't able to offer you much useful advice. It might just be my failings.

    I've gone through the last code snipped, and replaced everything that has no meaning to me with XXXXs, even after a few attempts to Google things (e.g. pawsu2pawju - Google Search )

    Code:
    void* pawRUN_initCB( void *ud )
    {
        XXXXXX Run = ud;
        XXXXXXX = Run;
        Run->tid = XXXXXXXXXXXX();
        if ( Run->initCB )
        {
            XXXXX *key = &(Run->env_key), *val = &(Run->env_val);
            XXXXXXXXXXX( key, Run->tid, XXXXXXXXXX );
            XXXXXXXXXXX( val, (XXXXX)Run, XXXXXXXXXXXX );
            XXXXXXX( key->txt, key->len, XXXXXXXXXX, sizeof(XXXXXXXXXX) - 1 );
            setenv( key->txt, val->txt, 1 );
            XXXXXXXXXXXXX( Run->id, Run );
            XXXXXXXXXXXXXXX( Run->id, XXXXXXXXXXXXX );
            Run->initCB( Run->id, Run->ud );
            unsetenv( key->txt );
        }
        return NULL;
    }
    
    
    static pthread_t pawrun_pthread( XXXXXX run )
    {
        XXXX txt = NULL;
        XXXXX key = XXXXXXXX, val = BAD_XXXXXXXX;
        XXXXX addr = 0;
        XXXXXX Run = NULL;
        XXXXXXXX( &key, run, XXXXXXXXXXX );
        XXXXXXX( key.txt, key.len, XXXXXXXXXX, sizeof(XXXXXXXXXX)-1 );
        txt = getenv( key.txt );
        XXXXXXX( val.txt, txt, XXXXXXXX(txt) );
        XXXXXXXXXXX( &addr, &val, XXXXXXXXXXXX );
        Run = (void*)addr;
        return Run->thread;
    }
    There really isn't much left that has any context for me, I'm sure others here feel the same way.

    I'm not asking for an explanation of what "pawsu2pawju()" does, or even what a "pawsu" or "pawju" is, I just want to make you aware that what you ask relies heavily on stuff nobody here knows, so you won't get meaningful answers.
    Last edited by hamster_nz; 10-10-2022 at 02:12 PM.

  11. #11
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Quote Originally Posted by hamster_nz View Post
    I just want to show you why people here aren't able to offer you much useful advice. It might just be my failings.

    I've gone through the last code snipped, and replaced everything that has no meaning to me with XXXXs, even after a few attempts to Google things (e.g. pawsu2pawju - Google Search )

    Code:
    void* pawRUN_initCB( void *ud )
    {
        XXXXXX Run = ud;
        XXXXXXX = Run;
        Run->tid = XXXXXXXXXXXX();
        if ( Run->initCB )
        {
            XXXXX *key = &(Run->env_key), *val = &(Run->env_val);
            XXXXXXXXXXX( key, Run->tid, XXXXXXXXXX );
            XXXXXXXXXXX( val, (XXXXX)Run, XXXXXXXXXXXX );
            XXXXXXX( key->txt, key->len, XXXXXXXXXX, sizeof(XXXXXXXXXX) - 1 );
            setenv( key->txt, val->txt, 1 );
            XXXXXXXXXXXXX( Run->id, Run );
            XXXXXXXXXXXXXXX( Run->id, XXXXXXXXXXXXX );
            Run->initCB( Run->id, Run->ud );
            unsetenv( key->txt );
        }
        return NULL;
    }
    
    
    static pthread_t pawrun_pthread( XXXXXX run )
    {
        XXXX txt = NULL;
        XXXXX key = XXXXXXXX, val = BAD_XXXXXXXX;
        XXXXX addr = 0;
        XXXXXX Run = NULL;
        XXXXXXXX( &key, run, XXXXXXXXXXX );
        XXXXXXX( key.txt, key.len, XXXXXXXXXX, sizeof(XXXXXXXXXX)-1 );
        txt = getenv( key.txt );
        XXXXXXX( val.txt, txt, XXXXXXXX(txt) );
        XXXXXXXXXXX( &addr, &val, XXXXXXXXXXXX );
        Run = (void*)addr;
        return Run->thread;
    }
    There really isn't much left that has any context for me, I'm sure others here feel the same way.

    I'm not asking for an explanation of what "pawsu2pawju()" does, or even what a "pawsu" or "pawju" is, I just want to make you aware that what you ask relies heavily on stuff nobody here knows, so you won't get meaningful answers.
    well the paw* calls I'm not expecting anyone to have an exact understanding of, just a rough idea from the code, basically just turning the thread id & the pointer into text that can be added to the environment variables to then be read later when needed, the prefix inserted is just for distinguishing the number from other variables that happen to use the same number (assuming it is the case is better than assuming it is not the case)

    Edit: Btw pawrun just maps to either HANDLE or thread id:
    Code:
    #ifdef PAW_ON_WINDOWS
    typedef void* pawrun;
    #define BAD_pawrun NULL
    #else
    typedef pawjd pawrun;
    #define BAD_pawrun -1
    #endif
    Wanted a reasonably short name for it and even if the typedef is not enough the typedef following it gives enough meaning about it's rough purpose:
    Code:
    typedef void* (*pawrun_cb)( pawd id, void *ud );
    typedef struct _PAW_THREAD PAW_THREAD, *pawRUN;
    typedef const PAW_THREAD *PAWRUN;
    I'll probably do a mass replace later to paw_thread but for now to help my fingers I'm using shorthands

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Data types and user input
    By john.joseph.law in forum C++ Programming
    Replies: 3
    Last Post: 09-19-2012, 03:14 AM
  2. Which is a better practice? (Global handle or finding handle)
    By C_Sparky in forum Windows Programming
    Replies: 4
    Last Post: 12-31-2010, 07:32 PM
  3. How to handle inputs if the user inputs the wrong thing
    By bassist11 in forum C Programming
    Replies: 5
    Last Post: 09-22-2010, 04:28 AM
  4. protect data from user
    By rogster001 in forum C Programming
    Replies: 8
    Last Post: 08-27-2010, 06:23 PM
  5. prompting user for data
    By radical in forum C Programming
    Replies: 5
    Last Post: 07-28-2005, 11:45 AM

Tags for this Thread