ThreadCtl(), ThreadCtl_r(), ThreadCtlExt(), ThreadCtlExt_r()

Control a thread

Synopsis:

#include <sys/neutrino.h>

int ThreadCtl( int cmd, 
               void * data );

int ThreadCtl_r( int cmd, 
                 void * data );

int ThreadCtlExt( pid_t pid,
                  int tid,
                  int cmd, 
                  void * data );

int ThreadCtlExt_r( pid_t pid,
                    int tid,
                    int cmd, 
                    void * data );

Arguments:

pid
(ThreadCtlExt() and ThreadCtlExt_r() only) The ID of the process whose thread you want to control, or 0 for the calling process.
Note: This argument must currently be 0.
tid
(ThreadCtlExt() and ThreadCtlExt_r() only) The ID of the thread you want to control, or 0 for the calling thread.
Note: You can execute _NTO_TCTL_IO and _NTO_TCTL_IO_PRIV only on the calling thread.
cmd
The command you want to execute; one of the following:

For more information, see below.

data
A pointer to data associated with the specific command; see below.

Library:

libc

Use the -l c option to qcc to link against this library. This library is usually included automatically.

Description:

These kernel calls allow you to make OS-specific changes to a thread. ThreadCtl() and ThreadCtl_r() always target the calling thread; ThreadCtlExt() and ThreadCtlExt_r() let you specify the process ID and thread ID.

Note: In order to target a command at a different thread, your process must have the PROCMGR_AID_XTHREAD_THREADCTL ability enabled for the given command. For more information, see procmgr_ability().

The ThreadCtl() and ThreadCtl_r() functions are identical except in the way they indicate errors. The same goes for ThreadCtlExt() and ThreadCtlExt_r(). See the Returns section for details.

The sections that follow describe the possible commands.

_NTO_TCTL_ALIGN_FAULT

ThreadCtl(_NTO_TCTL_ALIGN_FAULT, data)

This command controls the response to a misaligned access. The data argument must be a pointer to an int whose value indicates how you want to respond:

The function sets data to a positive or negative number, indicating the previous state of the alignment-fault handling.

Note:
  • Threads created by the calling thread inherit the _NTO_TCTL_ALIGN_FAULT status.
  • In QNX Neutrino 6.6 and later, on ARMv7 targets, the _NTO_TCTL_ALIGN_FAULT command is ignored; you can't control the alignment-fault behavior on a per-thread basis on these targets.

_NTO_TCTL_IO

ThreadCtl(_NTO_TCTL_IO, 0)

Superlock the process's memory and request I/O privileges; let the thread execute the in, ins, out, outs, cli, and sti I/O opcodes on architectures where it has the appropriate privilege, and let it attach IRQ handlers. If a thread attempts to use these opcodes without successfully executing this call, the thread faults with a SIGSEGV when the opcode is attempted. A lack of I/O privileges may also cause a SIGILL signal.

Note:
  • You can target this command only at the calling thread.
  • In order to use this command, your process must have the PROCMGR_AID_IO ability enabled. For more information, see procmgr_ability().
  • Threads created by the calling thread inherit the _NTO_TCTL_IO status.

_NTO_TCTL_IO_PRIV

ThreadCtl(_NTO_TCTL_IO_PRIV, 0)

This command is similar to _NTO_TCTL_IO, but _NTO_TCTL_IO_PRIV also requests that the thread be put into System (privileged) execution mode on ARM targets.

Note:
  • You can target this command only at the calling thread.
  • In order to use this command, your process must have the PROCMGR_AID_IO ability enabled. For more information, see procmgr_ability().
  • Threads created by the calling thread inherit the _NTO_TCTL_IO_PRIV status.

_NTO_TCTL_LOW_LATENCY

ThreadCtl(_NTO_TCTL_LOW_LATENCY, NULL)

(QNX Neutrino 7.0 or later) This flag hints to the scheduler that this thread needs to be scheduled on the same CPU as the one that the kernel is on when the scheduling decision is made. This saves the time it would take to schedule the thread on a different CPU.

For example, let's say a thread T is blocked on a call to InterruptWait(). When the interrupt that the thread is waiting on is asserted, the kernel executes a user-supplied Interrupt Service Routine. If the ISR sends a sigevent to the thread waiting on InterruptWait(), thread T is typically scheduled next. During this scheduling decision, if the kernel is on CPU 0, thread T could get selected to run on CPU 1, which would cause an interprocessor interrupt (IPI) and could waste valuable time. However, if you set _NTO_TCTL_LOW_LATENCY for thread T, it's scheduled to run on CPU 0 (the same CPU as the kernel).

Note that this flag doesn't determine whether a thread gets scheduled to run at all on any of the CPUs. That's determined by the runmask and the priority of the thread. This flag simply says that if the kernel does decide to schedule this thread on a CPU, that the CPU it chooses should be the same CPU as the kernel.

This flag and the thread's runmask could conflict. Suppose on a two-CPU system, a thread sets its runmask so that it can run only on CPU 0, and it also sets the _NTO_TCTL_LOW_LATENCY flag. If the kernel enters CPU 1, the thread isn't scheduled on CPU 1 because runmask settings are higher in rank than this flag. In this case, the thread is scheduled on CPU 0 if the priority of this thread is higher than what was running on that CPU (i.e., this thread flag is simply ignored). If you set this flag, don't set a runmask; let the thread run on any CPU if you want this flag to be strictly followed.

Note that priorities are completely separate from this flag. The scheduler will always schedule the highest priority thread that is in a runnable state. As such, if you really care about a particular thread's response time, make it runnable on any CPU, set its priority as the highest, and then set _NTO_TCTL_LOW_LATENCY in order to get the lowest latency possible (this is typically the case with threads handling high priority interrupts).

_NTO_TCTL_NAME

ThreadCtl(_NTO_TCTL_NAME, data)

(QNX Neutrino Core OS 6.3.2 or later) Set or retrieve the name of the current thread. The data argument must be a pointer to a _thread_name structure, which is defined as follows:

struct _thread_name {
    int     new_name_len;
    int     name_buf_len;
    char    name_buf[1];
};

The name_buf member is a contiguous buffer that extends the structure; name_buf_len is the size of this buffer.

If new_name_len is: The command:
Less than 0 Copies the thread's current name into name_buf, up to the number of bytes specified by name_buf_len
0 Deletes the thread's name
Greater than 0 Sets the thread's name to the value in name_buf, up to the number of bytes specified by name_buf_len

If you're setting or deleting the thread's name, the old name is copied as a NULL-terminated string into name_buf, up to the number of bytes specified by name_buf_len.

Note:

Here's an example:

#include <stdio.h>
#include <sys/neutrino.h>
#include <stdlib.h>
#include <string.h>

int main () {
    struct _thread_name *tname;
    int size;
    
    size = sizeof(*tname) * 2 + _NTO_THREAD_NAME_MAX * sizeof(char);
    tname = malloc (size);
    
    if (tname == NULL) {
        perror ("malloc");
        return EXIT_FAILURE;
    } else {
        memset (tname, 0x00, size);
        tname->name_buf_len = _NTO_THREAD_NAME_MAX;

        /* To change the name, put the name into name_buf and set
           new_name_len to the length of the new name. */

        strcpy (tname->name_buf, "Hello!");
        tname->new_name_len = strlen (tname->name_buf);

        if (ThreadCtl (_NTO_TCTL_NAME, tname) == -1) {
            perror ("ThreadCtl()");
            return EXIT_FAILURE;
        } else {
            printf ("The old name was: '%s'.\n", tname->name_buf);
        }

        /* To get the current name, set new_name_len to -1. */

        tname->new_name_len = -1;

        if (ThreadCtl (_NTO_TCTL_NAME, tname) == -1) {
            perror ("ThreadCtl()");
            return EXIT_FAILURE;
        } else {
            printf ("The current name is: '%s'.\n",
                    tname->name_buf);
        }

        /* To delete the name, set new_name_len to 0. */

        tname->new_name_len = 0;
        
        if (ThreadCtl (_NTO_TCTL_NAME, tname) == -1) {
            perror ("ThreadCtl()");
            return EXIT_FAILURE;
        } else {
            printf ("The old name was: '%s'.\n", tname->name_buf);
        }
        free (tname);
    }
    return EXIT_SUCCESS;
}

_NTO_TCTL_ONE_THREAD_CONT

ThreadCtl(_NTO_TCTL_ONE_THREAD_CONT, data)

(QNX Neutrino Core OS 6.3.2 or later) Unfreeze the thread with the given thread ID, which was frozen by an earlier _NTO_TCTL_ONE_THREAD_HOLD command. The data is the thread ID, cast to be a pointer (i.e. (void *) tid). This command returns an error of ESRCH if there's no thread with an ID of tid.

_NTO_TCTL_ONE_THREAD_HOLD

ThreadCtl(_NTO_TCTL_ONE_THREAD_HOLD, data)

(QNX Neutrino Core OS 6.3.2 or later) Hold the thread with the given thread ID in the calling process. The data is the thread ID, cast to be a pointer (i.e., (void *) tid). This command returns an error of ESRCH if there's no thread with an ID of tid.

This mechanism interacts with the SCHED_CONT_APP and SCHED_STOP_APP commands for SchedCtl() as follows:

_NTO_TCTL_RCM_GET_AND_SET

ThreadCtl(_NTO_TCTL_RCM_GET_AND_SET, data)

(QNX Neutrino 6.6 or later) Enter or leave resource-constrained mode. The data is a pointer to an integer; set the integer to 1 to entry constrained mode, or to 0 to leave it. The call stores the previous value in the memory pointed to by data:

int value = 1;
ThreadCtl(_NTO_TCTL_RCM_GET_AND_SET, &value); /* swaps current state with value */

/* Handle the request... */

ThreadCtl(_NTO_TCTL_RCM_GET_AND_SET, &value); /* restores original state */

For more information, see Resource constraint thresholds in the Processes chapter of the QNX Neutrino Programmer's Guide.

_NTO_TCTL_RUNMASK

ThreadCtl(_NTO_TCTL_RUNMASK, data)

Set the processor affinity for the calling thread in a multiprocessor system. The data is the runmask, cast to be a pointer (i.e., (void *) runmask). Each set bit in runmask represents a processor that the thread can run on.

By default, a thread's runmask is set to all ones, which allows it to run on any available processor. A value of 0x01 would, for example, force the thread to run only on the first processor. The new runmask takes effect immediately.

You can use _NTO_TCTL_RUNMASK to optimize the runtime performance of your system by, for example, relegating nonrealtime threads to a specific processor. In general, this shouldn't be necessary, since the QNX Neutrino realtime scheduler always preempts a lower-priority thread immediately when a higher priority thread becomes ready.

The main effect of processor locking is the effectiveness of the CPU cache, since threads can be prevented from migrating.

Note: Threads created by the calling thread don't inherit the specified runmask.

_NTO_TCTL_RUNMASK_GET_AND_SET

ThreadCtl(_NTO_TCTL_RUNMASK_GET_AND_SET, data)

(QNX Neutrino 6.3.0 SP2 or later) Get and set the runmask (the processor affinity) to a proper value for the calling thread in a multiprocessor system. The data parameter is a pointer to the runmask. On input, the pointer to value is used to set the new runmask for the thread (see _NTO_TCTL_SET_RUNMASK for details). The new runmask takes effect immediately. After the function has completed, the contents of *data will be replaced with the previous runmask for the thread. Calling ThreadCtl again with the same pointer will restore the runmask to the state before the call.

_NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT

ThreadCtl(_NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT, data)

(QNX Neutrino Core OS 6.3.2 or later) Manipulate the calling thread's runmask (the processor affinity) and inherit mask (the runmask to use for the process's children). The data argument must be a pointer to a struct _thread_runmask:

struct _thread_runmask {
	int			size;
	/*	unsigned	runmask[size];		*/
	/*	unsigned	inherit_mask[size];	*/
};

The size of the masks (and hence the size of the structure) depends on the number of processors on your system. We've defined the following macros to make it easier for you to work with this structure:

RMSK_SIZE(num_cpu)
Determine the number of integers required to hold each mask, given the number of CPUs on your system (which you can find in _syspage_ptr->num_cpu).
RMSK_SET(cpu, p)
Set the bit for cpu (where cpu is zero-based) in the mask p.
RMSK_CLR(cpu, p)
Clear the bit for cpu (where cpu is zero-based) in the mask p.
RMSK_ISSET(cpu, p)
Determine the value of the bit for cpu in the mask p.

The p argument must be a pointer of type unsigned * to the appropriate mask. For more information about using these macros, see Thread affinity in the Multicore Processing chapter of the QNX Neutrino Programmer's Guide.

The _NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT command saves the values for both masks at the time of the call in their respective members of this structure. If you pass 0 for the masks, the masks are left unaltered; otherwise they're set to the specified value(s). The new runmasks take effect immediately.

Here's an example:

#include <sys/neutrino.h>
#include <sys/syspage.h>
#include <malloc.h>
#include <stdio.h>

int
main(void)
{
   int *rsizep, rsize, size_tot;
   unsigned *rmaskp, *inheritp;
   unsigned buf[8];
   void *freep;

   /*
    * struct _thread_runmask is not
    * uniquely sized, so we construct
    * our own.
    */
   
   rsize = RMSK_SIZE(_syspage_ptr->num_cpu);

   size_tot = sizeof(*rsizep);
   size_tot += sizeof(*rmaskp) * rsize;
   size_tot += sizeof(*inheritp) * rsize;

   if (size_tot <= sizeof(buf)) {
      rsizep = buf;
      freep = NULL;
   }
   else if ((rsizep = freep = malloc(size_tot)) == NULL) {
      perror("malloc");
      return 1;
   }

   memset(rsizep, 0x00, size_tot);

   *rsizep = rsize;
   rmaskp = (unsigned *)(rsizep + 1);
   inheritp = rmaskp + rsize;

   /*
    * Both masks set to 0 means get the current
    * values without alteration.
    */

   if (ThreadCtl(_NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT,
                 rsizep) == -1) {
      perror("_NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT");
      free(freep);
      return 1;
   }

   /*
    * Restrict our inherit mask to the last cpu; leave the
    * runmask unaltered.
    */
   
   memset(rsizep, 0x00, size_tot);
   *rsizep = rsize;
   RMSK_SET(_syspage_ptr->num_cpu - 1, inheritp);

   if (ThreadCtl(_NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT,
                 rsizep) == -1) {
      perror("_NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT");
      free(freep);
      return 1;
   }

   free(freep);
   return 0;
}

_NTO_TCTL_SHR_MUTEX

ThreadCtl(_NTO_TCTL_SHR_MUTEX, data)

(QNX Neutrino 7.0 or later) This command controls how a thread shares mutexes with threads in different processes. The data argument must be a pointer to an unsigned int whose value is one of the following:

The default setting for procnto's threads is 0; for threads in other processes, the default is _NTO_TF_SHR_MUTEX. If your thread doesn't intend to share any of its mutexes with threads in other processes, you can improve security by calling:

unsigned int value = 0;
ThreadCtl( _NTO_TCTL_SHR_MUTEX, (void *)&value);

_NTO_TCTL_THREADS_CONT

ThreadCtl(_NTO_TCTL_THREADS_CONT, 0)

Unfreeze all threads in the current process that were frozen using the _NTO_TCTL_THREADS_HOLD command.

_NTO_TCTL_THREADS_HOLD

ThreadCtl(_NTO_TCTL_THREADS_HOLD, 0)

Hold all threads in the current process except the calling thread.

Note: Threads created by the calling thread aren't frozen.

Blocking states

These calls don't block.

Returns:

The only difference between ThreadCtl() and ThreadCtl_r() and between ThreadCtlExt() and ThreadCtlExt_r() is the way they indicate errors:

ThreadCtl(), ThreadCtlExt()
If an error occurs, these functions return -1 and set errno. Any other value returned indicates success.
ThreadCtl_r(), ThreadCtlExt_r()
These functions return EOK on success. They don't set errno. If an error occurs, they may return any value listed in the Errors section.

Errors:

E2BIG
The name is larger than the accepted size.
EINVAL
An argument is invalid; the possible cause could be one of the following:
  • The name buffer length is invalid or smaller than the new name length.
  • The specified runmask is invalid.
  • The process ID passed to ThreadCtlExt() or ThreadCtlExt_r() isn't 0.
ENOTSUP
You tried to execute a _NTO_TCTL_IO or _NTO_TCTL_IO_PRIV command on a thread other than the calling thread.
EPERM
The calling process doesn't have the required permission; if you're using ThreadCtlExt(), ThreadCtlExt_r(), or _NTO_TCTL_IO, see procmgr_ability(). Not even root can target a command at a thread in a different process.
ESRCH
There's no thread with an ID of tid.

Classification:

QNX Neutrino

Safety:  
Cancellation point No
Interrupt handler No
Signal handler Yes
Thread Yes