Thread synchronization: Condvar

A condvar (condition variable) is used together with a mutex lock. It blocks a thread within a critical section until some condition is satisfied.
Initialization and destroy
  •  pthread_condattr_init(pthread_condattr_t *attr;);
  • pthread_cond_init(pthread_cond_t *condv, pthread_condattr_t *attr);
  • pthread_cond_destroy(pthread_cond_t *condv);
Condvar attributes
  • A condvar can be PTHREAD_PROCESS_PRIVATE(default) or PTHREAD_PROCESS_SHARED.
    • pthread_condattr_setshared(&attr, int shared);
    • pthread_condattr_getshared(&attr, &shared);
  •  A condvar can use different clocks: CLOCK_REALTIME, CLOCK_SOFTTIME, CLOCK_MONOTONIC (not affected by changes to the system time). It will be used to for pthread_cond_timewait(). The default value refers to the system clock??
  • pthread_condattr_setclock(&attr,clock_id)
  • pthread_condattr_getclock(&attr, *clock_id);
Wait
  • pthread_cond_wait(&condv, &mutex);
    • this function blocks the calling thread on the conditional variable (condv), and unlocks the associated mutex (mutex).
    • the thread is blocked until
      • another thread calls pthread_cond_signal() or pthread_cond_boradcast(), or
      • a signal is delivered to the thread, or
      • the thread is canceled.
        • In all cases, the thread reacquires the mutex before being unblocked. It means, even another thread calls _signal(), or _broadcast(), this thread might still be blocked, until the other thread calls pthread_mutex_unlock() to release the mutex.
  • pthread_cond_timedwait(&condv, &mutex, const struct timespec *abstime);
    •  returns
      • EOK, if success, or was interrupted by a signal.
      • ETIMEOUT if timeout.
      • EPERM: the thread doesn’t own the mutex.
      • If the thread is cancelled, the cleanup handler is executed, after the thread reacquires the mutex.
Unblock
  • pthread_cond_signal(&cond): unblock the highest priority thread.
  • pthread_cond_broadcast(&cond): unblock all the threads.
Useful tips
  • A thread must lock mutex first,  then wait for condv (On return from the function, the mutex is again locked and owned by the called thread).
  • Don’t use a recursive mutex with condition variables.
  • Make sure that the thread’s cleanup handlers unlock the mutex.
  • use while loop while waiting for a condvar. reasons:
    • POSIX cannot guarantee no false wakeups (e.g. Multiprocessor systems);
    • we need test to ensure the condition matches our criteria.
  • pthread_cond_timedwait() uses struct timespec to specify an absolute timeout value.
    • Use clock_gettime(clock_id, &ts) to get the system time
      • Don’t use the kernel call ClockTime_r(clock_id, const &new, &old)
  • More on get time funtions
    • ClockTime_rreturns the time in nano seconds, while clock_gettime() returns struct timespec.
    • two useful functions:
      • void nsec2timespec(&tp, nsec);
      • uint64_t timespec2nsec(&tp);
 Example(30ms timeout)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
ts.tv_nsec += 30 * 1000000ULL;
pthread_mutex_lock(&mutex);
// form 1
while (pthread_cond_timedwait(&condv, &mutex, &ts) == EOK) {
// get the condv, or interrupted by a signal
   If(condition is true) {
        Break;
   }
}
// an error returned by pthread_cond_timedwait(), possibly ETIMEOUT.
// form 2
While(contition is false) {
   If(pthread_cond_timedwait(&condv, &mutex, &ts) != EOK) {
    Break;
   }
}
pthread_mutex_unlock(&mutex);
….
Advertisements

Artifacts(1): alias and chromatic aberration

SChromatic aberration/distortion
Chromatic aberration occurs along boundaries that separate dark and bright parts of the image.
  • Reason: the lens fails to focus all colors to the same convergence point.
  • avoid: It can be reduced by inrceasing the focal length of the lens.
Spatial alias
 an effect that causes different signals to become indistinguishable, which can occur in sampling or reconstruction process. If the image data is not properly processed, the reconstructed image will differ from the original image. Usually introduced when representing a high-resolution image at a lower resolution.
Spatial anti-aliasing
Remove higher signal components which can’t be handled properly before sampling at a lower resolution.
Anti aliasing makes polygon edges not jaggy but smoothed out. The level of anti-aliasing determines how smooth polygon edges are.

Pulse

Message and pulse are QNX native IPC, no POSIX/UNIX.
Usually, message passing is synchronous:  after MsgSend(), the client blocks(send-blocked, or reply-blocked) until the server gets around to replying.
If you want asynchronous messaging,
  1. if you need to transfer data
    1. Have the server dedicate a thread to receive and immediately reply messages, minimizing the blocking interval.
    2. Have the client send the data via a thrad
    3. Use POSIX message queue
  2. If you don’t need to transfer data
    1. Posix signal
    2. pulse
Pulse is asynchronous. Pulse makes “a non-blocking send” possible, which works for the scenario where senders can’t afford to block.
Pulse has fixed payload size (32 bit value, 8 bit code)
Pulse initialization/send/receive
  • SIGEV_PULSE_INIT(&event, coid, priority, pulse_code, pulse_value);
    • If you want the thread that receives the pulse to run at the initial priority of the process, set priority to SIGEV_PULSE_PRIO_INHERIT.
  • Int MsgSendPulse(coid, priority, pulse_code, pulse_value)
  • Int MsgRecivePulse(chid, &pulse, sizeof(pulse), struct _msg_info *info);  // info should be NULL for pulse.
  • Int MsgReceive(chi, &msg, bytes, struct _msg_info *info);       // a return of “0” means a pulse is received; if >0, a normal message.

Priority Inversion: a low-piority thread consumes all available CPU time, even though a higher-priroity thread is ready to run.

To avoid it, the solution is Priority inheritance, or Message-driven priority: A server process receives messages & pulses in priority order. the receiving thread within the server process inherits the priority of the sender thread; when it receives messages (for pulses, receiving thread will run at the priority of the pulse).

Assume there are 3 threads, t1 (priority 13), t2 (priority 10), server thread (priority 22),

  • If the sender(t2) is of lower priority (10) than the receiver (22), the effective priority of the server thread changes when the message is received (on MsgReceive).
    • the sender(t2)’s work is done at priority 10.
    • If another higher priority thread(t1) is running when t2 calls MsgSend(), it will not preempted by t2.
  • If the sender(t1) is of higher priority(13) than the receiver(10), the effective priority of the server thread changes on MsgSend().
    •  If multiple clients are SEND blocked, server will inherit the priority the higher client.
  • After the server reply to t1 and t2, its priority will still be 13, and receive blocked.
 Note
  • Priority inheritance can be turned off by channel flag _NTO_CHF_FIXED_PRIORITY. If the channel is created with _NTO_CHF_FIXED_PRIORITY, receiving threads won’t change priorities to those of the sending threads (does it work for pulses???)
  • For pulses (an example of pulse priority)
    • Pulse have a priority value while being initialized. If you want the thread that receives the pulse to run at the initial priority of the process, set sigev_priority to SIGEV_PULSE_PRIO_INHERIT (note: not the priority of the thread which sends the pulse, but the initial priority of the process!???)
    • Sender can specify the priority of the pulse, or –1 to use the priority of the calling thread.
Example
#include <sys/neutrino.h>
#include <sys/netmgr.h>
#define MY_PULSE PULSE_CODE_MINAVAIL+2
 
int chid = ChannelCreate(unsigned flags);
Int coid = ConnectAttach(node_id, pid, chid, 0 | _NTO_SIDE_CHANNEL, flags);
Struct sigevent event;
SIGEV_PULSE_INIT(&event, coid, prio, MY_PULSE, NULL);
In a thread,
struct _pulse pulse;
If(MsgReceivePulse(chid, &pulse, sizeof(pulse), NULL) != -1) {  //similar to MsgReceive(), but only receives pulses.
    Switch(pulse.code) {
        Case MY_PULSE:
        Break;
    Default:
        …..
    }
}
In the other thread,
MsgSendPulse(coid, -1, MY_PULSE, 0);

Thread synchronization: mutex

How does mutex work?
  • Only one thread may have the mutex locked at any given time.
  • Threads blocks if the mutex is already owned by another thread.
  • When the mutex is unlocked, the highest-priority thread will unblock and become the new owner of the mutex.
Note: in QNX, On most processors, acquiring a free/unlocked mutex doesn’t require entry to the kernel — the OS/kernel is only used to resolve contention. Therefore, acquisition and rlease of an uncontested critical section or resouce is very quick. The kernel is only involved:
  • to put the waiting thread to a blocked list if the mutex is being held, and
  • to unblock a thread if it is of the highest priority thread in the waiting list, when the mutex is being released.
(default) mutex attributes
  • PTHREAD_PRIO_INHERIT  The priority of the thread, which has locked the mutex, is raised to the highest priority of the threads, which are blocked, waiting on this mutex.
  • PTHREAD_RECURSIVE_DISABLE   any thread that tries to lock an already locked mutex becomes blocked.
Mutex administration
  •  initialize a statically allocated mutex, with the default attributes
    • pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
  • a normal initialization/destroy of mutex
    •  int pthread_mutex_init(&mutex, &attr); // if attr is NULL, default attributes.
    •  int pthread_mutex_destroy(&mutex)
  • play attribute values
    • pthread_mutexattr_get/set…(&attr, xx);
Using muex
  • Int pthread_mutex_lock(&mutex)  // the thread blocks until it has accquired the mutex.
  • pthread_mutex_timedlock(&mutex, struct timespec *ts_timeout);
  • pthread_mutex_timelock_monotonic(&mutex, struct timespec *ts_timeout)); // not affected by changes to the system time.
  • Int pthread_mutex_unlock(&mutex);
  • Int pthread_mutex_trylock(&mutex); // not block the thread if the mutex is already locked. The return value “EBUSY” means the mutex is already locked.

Thread creation/termination

Thread Creation

int pthread_create(&tid, &attr, func, &arg);
Thread destroy
  1. in a different thread
    • int pthread_cancel( tid );  // tid can be pthread_self()
      • the cancellation type and state of the target thread determine when the cancellation takes effect.
    • int pthread_join(tid, **value_ptr);
    • int pthread_abort(tid);  // tid can be pthread_self()
      • termination takes effect immediately (don’t wait for a cancellation point).
      • no cancellation handler called.
      • resources are not released (mutexes, file descriptors, etc)
  2. in the thread itself
    • void pthread_exit(*value_ptr);
      • value_ptr: thread’s exit status, which carries whatever you want the other thread (e.g. the thread calls pthread_join()) to know.
      • when the thread start routine returns, pthread_exit() is called implicitly.
    • void pthread_cleanup_push(cleanup_handler, &arg)
    • void pthread_cleanup_pop(execute)  // execute = 0, means the handler will not be executed.
      • the cleanup handler is pop/executed when the thread:
        • exits
        • acts on a cancellation request.
        • calls pthread_cleanup_pop() with nonzero.
      • NOTE! the cleanup handler should support multiple entrances.
#include <pthread.h>
struct my_context;
void *my_thread1(void *arg)
{
    struct my_context *cx=(struct my_context *)arg;
    ….
    return NULL; // pthread_exit() is called implicitly
}
void my_cleanup(void *args)
{
    struct my_context *ctx = (struct my_context *)args;
    if(ctx->iid != -1) {
        InterruptDetach(ctx->iid);
    }
}
void *my_thread2(void *arg)
{
    struct my_context *cx=(struct my_context *)arg;
    ctx->iid = InterruptAttachEvent(…);
    pthread_cleanup_push(my_cleanup, ctx);
    ….
    pthread_cleanup_pop(1);
    return NULL;
}
int main(int argc, char **argv)
{
    struct my_context *ctx;
    // create a thread with default attributes.  Don’t care about thread ID.
    // pthread_create(NULL, NULL, my_thread1, ctx);
    // create a thread with priority set to +20
    pthread_t tid;
    pthread_attr_t attr;
    struct sched_param param;
    int allowable_prio;
    pthread_attr_init(&attr);
    pthread_getschedparam(pthread_self(), NULL, &param);
    allowable_prio = sched_get_priority_adjust(-1, SCHED_NOCHANGE, +20);
    param.sched_priority = allowable_prio;
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    // pthread_attr_setschedpolicy(&attr, policy);  is this required?
    pthread_attr_setschedparam(&attr, &param);
    pthread_create(&tid, &attr, my_thread1, ctx); // all pointers
pthread_join(tid, NULL);  // wait/block until the target thread(tid) has terminated.
}

Thread atributes

 Process and thread: processes own resources, threads run code.
(Default) thread attributes
  • PTHREAD_CREATE_JOINABLE: the thread is put into a zoombie state when it terminates, until you retrieve its exit status or detach it.
  • inherit-scheduling attribute
    • PTHREAD_INHERIT_SCHED: the thread inherits the scheduling policy of its parent thread.
    • another value: PTHREAD_EXPLICIT_SCHED
      • if we want to use different scheduling parameters for the new thread, this need to be changed to PTHREAD_EXPLICIT_SCHED.
  • cancellation state
    • PTHREAD_CANCEL_ENABLE: cancellation requests may be acted on accordingly to the cancelation type
    • another state: PTHREAD_CANCEL_DISABLE
  • cancellation type
    • PTHREAD_CANCEL_DEFERRED: cancellation requests are held pending until a cancellation point.
    • PTHREAD_CANCEL_A
  • 4K stack

Thread scheduling

  1. Scheduling policy: inherited from the parent process (PTHREAD_INHERT_SCHED), by default. In all cases, a higher-priority thread can preempts all lower-priority threads one it gets READY.
    • FIFO scheduling: apply only when two or more threads that share the same priority are READY.
    • Round-robin scheduling: a thread stop executing when it consumes its timeslice.
    • Sporadic scheduling
  2. Scheduling priority, independent of the scheduling policy. The scheduler selects the highest priority thread to run, from the READY threads.
    • range 0 ~ 255
      • 0 is for the special idle thread (in kernel), which is always ready to run.
      • 1 ~ 63 for user threads. (the range can be changed by pronto -P option).
      • 1 ~255 for root threads.

Data structure

  • pthread_attr_t: stack size, detachstate, schedparam, schedpolicy, etc
  • struct sched_param: contains sched_priority, sched_curpriority
  • int policy: FIFO(1), RR(2, default?), OTHER(3), SPORADIC(4)

 APIs

  1. process level
    • Get/set the priority of a process; if pid 0 used, it is for current process.
      • sched_setparam(pid|0, &sched_param): 
      • sched_getparam(pid|0, &sched_param);
    • get the priority range for the scheduling policy
      • sched_get_priority_max(int policy);
      • sched_get_priority_min(int policy);
    • calculate the allowable priority for the policy
      • allowable_priority = sched_get_priority_adjust(int priority, int policy, int adjust);
        • priority = -1, priority of current thread.
        • policy=SCHED_NOCHANGE, the policy of the called thread
        • adjust=+20, we want a priority of “priority + 20”.
  2. thread level (used after the thread is created)
    • Set/get scheduling parameters
      • pthread_getschedparam(tid, &policy, &param);
      • pthread_setschedparam(tid, policy, &param)
    • set the priority
      • pthread_setschedprio(tid, prio);
      • Note: the priority get should be achieved using pthread_getschedparam().
    • set cancellation state/type
      • pthread_setcancelstate(int newstate, &oldstate)
      • pthread_setcanceltype(int newtype, &oldtype);
        • not _get functions.
        • can only be set after thread creation.
  3. attribute level (only take effect before the thread is created)
    • initialize/destroy attr object
      • pthread_attr_init(&attr)
      • pthread_attr_destroy(&attr);  // the memory is not freed. but after destroying, reinit the object using pthread_attr_init()
    • set/get policy
      • pthread_attr_setschedpolicy(&attr, int policy)
      • pthread_attr_getschedpolicy(&attr, &policy)
    • set/get scheduling parameters
      • pthread_attr_setschedparam(&attr, &param);
        • used only after pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
      • pthread_attr_getschedparam(&attr, &param);
    • set/get inherit-scheduling attributes
      • pthread_attr_setinheritsched(&attr, interitsched);
      • pthread_attr_getinheritsched(&attr, &interitsched);
        • need to be set prior to the thread creation

Video decoder basics

  1. AFE (analog Front End): pre-process and digitizes the analog signal.
  2. Automatic gain control (via a variable gain amplifier) on Luma or Chroma
    1. For luma, AGC may operate based on the depth of the horizontal sync pulse, or a fixed manual gain, etc.
    2. For chroma, AGC may operate based on the depth of the horiontal sync pulse on the luma channel, etc
  3. Color killer: usually there is a threshold, if the color carrier of the incoming video signal is less than the threshold,  the decoder switchs off the color processing.
  4. Clamping circuit: restore the proper DC level.
  5. Video characteristic adjustement
    1. Brightness
    2. Contrast
    3. Color saturation
    4. Hue
    5. Sharpness
    6. CTI (color transient improvement): to enhance the color bandwidth