A course that is not concerned with MATLAB, great.


Table of Contents

  1. Lec 1. Programming in C language
  2. Course structure
  3. System software
  4. Multithreaded programming
  5. Introduction of Linux an C
  6. Kinds of Typing and examples
  7. Lec 2. As above
  8. Lec 3. Posix
    1. Pros and Cons of Multithreading
      1. Cost of Multithreading
    2. Threading models
      1. Kernel-level threading
      2. User-level threading
      3. Hybrid threading
      4. Coroutines and Fibers
    3. Threading Patterns
      1. Thread-per-Connection
      2. Event-Driven Threading
    4. Race Conditions
    5. Synchronization
    6. Mutexes - mutually exclusive access
    7. Deadlocks
    8. Creating threads
    9. Terminating Threads
    10. Joining thread
    11. Detaching threads
    12. Initializing mutexes
    13. Locking / Unlocking mutexes
    14. Conditional variable
    15. Wait

Well to be clear that I’m not meaning that MATLAB is awful, it’s just too ‘perfect’. All functions within are well encapsuled. A lack of interacting I suppose.

Lec 1. Programming in C language

Course structure

  • the main tool for system programming – the C programming language;

  • (2) multithreaded programming with POSIX Threads;

  • (3) Linux operating system basics and shell;

  • (4) regular expression language.

I have to admit that after seeing this, I’m super excited. Just right about what I wanted to learn.

System software

is a set of software that is a platform for the development and operation of other programs. For example, these are operating systems such as Windows, Linux, utilities (chmod, ipconfig, grep), game engines, programming tools

Layers can be seen at page 3.

Multithreaded programming

Multithreaded programming means program organization that can execute parallel operations (threads) in one process.

Introduction of Linux an C

Just as name suggest.

Some showcase of Assemly language optimization

Kinds of Typing and examples

Static typing - checking the correctness of expressions occurs at the compilation stage.

Dynamic typing - works at run time.

Strong typing means that we cannot perform operations on data that could lead to run-time errors due to data inconsistency or incompatibility.

Weak typing allows the ability to implicitly convert data from type to type.

Explicit typing − we explicitly specify data types.

Implicit typing – the compiler itself can determine the data type from the expression.

OCaml – static and strong-typed programming language

float + float

int + int

40 + 2.0 – error

Java – static and strong-typed programming language, BUT

40 + 2.0 = 42.0 //compiler automatically casts 40 (integer) to 40.0 (double)

Python – dynamic and strong-typed, duck-typed

Duck-typing means that variables are untyped, but objects (data) are has data types.

x = 1.0 + 2 #it is okay

JavaScript – dynamic and weakly-typed

x = ‘1’ + 1; //x has value ’11’ (string).

As for grammer of the C, I think I’m pretty familiar with most of common ones, for which I’ll pass.

But sometimes I gotta remember the corresponding notations in English.


Lec 2. As above

Focus on

  • pointers
  • memory allocation / leakage
  • Directives
  • Some utilities

Lec 3. Posix

It’s a part of network programming.

Thread is a single sequential flow of control within a program

Pros and Cons of Multithreading

  • Pros
    • Programming abstraction
    • Parallelism
    • Improving responsiveness
    • Blocking I/O
      • Multiplexed I/O
      • Nonblocking Reads
      • Asynchronous I/O
    • Context switching
    • Saving memory

Cost of Multithreading

Limitation of the performance growth $S_p$ of a computing system with an increase in the number of processors $p$

where, $S_p=Speedup$, $a=\frac{seqential}{sequential+parrallel}$,

Threading models

There are several approaches to the implementation of multithreading in a system with varying degrees of functionality provided by the kernel in user space.

  • User threads : managed without kernel support
  • Kernel threads : managed directly by OS
  • Relationship b/w user and kernel thread
    • Many-to-one model
    • One-to-one model
    • Many-to-many model
  1. Kernel-level threading

    Simplest. One-to-one.

  2. User-level threading

    Contra the first one. Many-to-one model.

  3. Hybrid threading

    Many to many. combines preivous advantages, native threading support.

  4. Coroutines and Fibers

    They provide a unit of execution even lighter in weight than the thread. For them scheduling and execution almost does not require user space support. They’re scheduled together, requiring only a clear transition from one to another.

    They’re more needed to control program execution than to execute parallelism.

Threading Patterns

  • Thread-per-Connection

    one thread is assigned to one execution item.

    This thread is assigned to no more than one work item throughout all the work

  • Event-Driven Threading

    Event-driven approach can separate threads from connections, which only use threads for events on specific callbacks or handlers.

Race Conditions

A race condition occurs when two or more threads can access shared data and they try to change it at the same time. Because the thread scheduling algorithm can swap between threads at any time, you don’t know the order in which the threads will attempt to access the shared data. Therefore, the result of the change in data is dependent on the thread scheduling algorithm, i.e. both threads are “racing” to access/change the data.

So, thread safety needs to be ensured.

Synchronization

The fundamental source of races is that critical regions are a window during which correct program behavior requires that threads do not interleave execution. To prevent race conditions, then, the programmer needs to synchronize access to that window, ensuring mutually exclusive access to the critical region.

In computer science, we say that an operation (or set of operations) is atomic if it is indivisible, unable to be interleaved with other operations. To the rest of the system, an atomic operation (or operations) appears to occur instantaneously. And that’s the problem with critical regions: they are not indivisible, they don’t occur instantaneously, they aren’t atomic.

Mutexes - mutually exclusive access

Only one person (thread) can enter the room (resource) if he has a key (lock).

There is only one key. So at the same time only one thread can get the resource.

Deadlocks

A deadlock is a situation in which two threads are waiting for each other to finish, and thus neither does. In the case of mutexes, a deadlock occurs if two threads are both waiting for a different mutex, which the other thread holds.

POSIX Threads

  • pthread POSIX standard tells us how threads should work on UNIX OS

important calls:

  • pthread_create : Create a new thread; analogous to fork
  • pthread_exit : Terminating the calling thread. Analogous to exit , has return value
  • pthread_join : Wait for another thread to exit. Analogous to wait - the caller cannot proceed other thread exists
  • pthread_yield : Release the CPU, let another thread run.
  • pthread_attr_init : Create and initialize a thread’s attributes. Contains info like priority of thread
  • pthread_attr_destroy : Remove a thread’s attributes. Free up the memory holding the attribute info.
  • pthread_cancel : Signal cancellation to a thread. Can be asynchronous or deferred, depending on thread attributes

The pthread API is defined in . Every function in the API is prefixed by pthread_. For example, the function to create a thread is called pthread_create(). Pthread functions may be broken into two large groupings:

Thread management

Functions to create, destroy, join, and detach threads.

Synchronization

Functions to manage the synchronization of threads, including mutexes, condition variables, and barriers.

Creating threads

When your program is first run and executes the main() function, it is single threaded. Indeed, other than the compiler enabling some thread safety options and the linker linking in the Pthreads library, your process isn’t any different from any other. From this initial thread, sometimes called the default or master thread, you must create one or more additional threads to become multithreaded.

1
2
3
4
5
#include <pthread.h>
int pthread_create (pthread_t *thread
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg);

Terminating Threads

Threads may terminate under several circumstances, all of which have analogues to process termination:

If a thread returns from its start routine, it terminates. This is akin to “falling off the end” of main().

If a thread invokes the pthread_exit**() function (discussed subsequently), it terminates. This is akin to calling exit().

If the thread is canceled by another thread via the pthread_cancel**() function, it terminates. This is akin to being sent the SIGKILL signal via kill().

1
2
3
// Terminating the current thread
#include <pthread.h>
void pthread_exit(void *retval);

Upon invocation, the calling thread is terminated. retval is provided to any thread waiting on the terminating thread’s death, similar to exit().

1
2
3
4
5
//Terminating other threads
#include <pthread.h>
int pthread_cancel (pthread_t thread);
int pthread_setcancelstate(int state, int *oldstate);
int pthread_setcanceltype(int type, int *oldtype);

On success, pthread_ cancel() returns zero.

On error, pthread_cancel() returns ESRCH, indicating that thread was invalid.

Threads can change their state via pthread_set cancelstate():

Threads can change their type via pthread_setcanceltype():

Joining thread

Joining allows one thread to block while waiting for the termination of another:

1
2
#include <pthread.h>
void pthread_exit(void *retval);

Detaching threads

By default, threads are created as joinable. Threads may, however, detach, rendering them no longer joinable. Because threads consume system resources until joined, just as processes consume system resources until their parent calls wait(), threads that you do not intend to join should be detached.

1
2
#include <pthread.h>
void pthread_detach(pthread_t thread);

Initializing mutexes

Mutexes are represented by the pthread_mutex_t object. Like most of the objects in the Pthread API, it is meant to be an opaque structure provided to the various mutex interfaces. Although you can dynamically create mutexes, most uses are static:

1
2
#include <pthread.h>
pthread_mutex_t mutex = PHTHREAD_MUTEX_INITIALIZER;

Locking / Unlocking mutexes

Locking (also called acquiring) a Pthreads mutex is accomplished via the pthread_mutex_lock() function,

The counterpart to locking is unlocking, or releasing, the mutex.

1
2
3
#include <pthread.h>
int pthread_mutex_lock (pthread_mutex_t *mutex);
int pthread_mutex_unlock (pthread_mutex_t *mutex);

Conditional variable

Synchronization mechanisms need more than just mutual exclusion; also need a way to wait for another thread to do something (e.g., wait for a character to be added to the buffer)

wait(condition, lock): release lock, put thread to sleep until condition is signaled; when thread wakes up again, re-acquire lock before returning.

signal(condition, lock): if any threads are waiting on condition, wake up one of them. Caller must hold lock, which must be the same as the lock used in the wait call.

broadcast(condition, lock): same as signal, except wake up all waiting threads.

Note: after signal, signaling thread keeps lock, waking thread goes on the queue waiting for the lock.

Warning: when a thread wakes up after cond_wait there is no guarantee that the desired condition still exists: another thread might have snuck in.

Wait

The pthread_cond_wait() routine always returns with the mutex locked and owned by the calling thread, even when returning an error.

This function blocks until the condition is signaled. It atomically releases the associated mutex lock before blocking, and atomically acquires it again before returning.


Some additional discovery about usage of * in terminal if under zsh

Follow error will be displayed by default when * is used to match files or some others.

1
$ clang-tidy: No match -checks=-*,clang-analyzer-*,-clang-analyzer-cplusplus*

It’s actually a limitation in zsh, which could save you in case you just typed rm * . Surely that is dangerous.

And please, remember that exit works for the whole program, don’t use it in the signal_handler for certain thread.


[Thu Apr 7 2022, 22:54] Some toolchain support

i clang-format

i fmenezes/tap/clang-tidy