Suspend Thread on Linux (corrected version at the bottom of the article)

Since there was no Suspend Thread-like API on Linux, try suspend / resume the thread by force with mutex.

For the time being, I've implemented it twice, but I've pointed out that I don't have much knowledge about the API implementation of the OS, and I've been eating it twice, so I'm crazy about it, so will I be honest for the third time or will it happen twice?

Summary of points to be pointed out so far

Points from @drab

  1. You can use "pthread_cond_wait" or "fifo or message queue".
  2. Using pthread_cond_wait in code that does something with a keystroke event is likely out

Points to be pointed out by @ angel_p_57

  1. Absolutely do not release mutex from other threads.
  2. There is a misunderstanding about how to use pthread_cond_ * (it is NG to use it just to wait for such an event!)

Based on these points, what I wanted to do in the first place.

When implementing message processing or something like that When you want to process the queued messages continuously after putting them in the queue (in short, asynchronous processing such as sending to the NW side).

I want to suspend a thread at a specific point because it is a waste of resources that loops wastefully when there is no message. In short, I want to wait for the event.

Compare what you pointed out with what you want to do

Pointed out: Using pthread_cond_wait in code that does something with a keystroke event is likely to be out. What I want to do: I want to wait for an event.

Out.

~~ A memo to pause using Mutex in such a case. ~~

~~ ** 2017/04/17 I wrote it with the correct implementation method according to the pointed out matter. (See the bottom of the article) ** ~~ ** 2017/04/21 Corrected again **

Uncode full of mistakes that I wrote at the beginning

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <pthread.h>
#include <unistd.h>
#include <stdarg.h>
#include<stdint.h>
#include<assert.h>

//Put a string on the console
int consoleWrite(const char *fmt, ...)
{
    va_list list;
    va_start(list, fmt);
    int writed = vprintf(fmt, list);
    fflush(stdout);
    va_end(list);
    return writed;
}

//Accept strings from the keyboard
size_t input(char *buf, size_t bufLen)
{
#define GabageBufSize 0x100
    uint8_t bin[GabageBufSize];
    char *savePtr = NULL;
    char *token = NULL;
    static_assert(sizeof(bin) == GabageBufSize, "Gabage buffer size not 255");

    fgets(buf, bufLen, stdin);
    token = strchr(buf, '\n');
    if (token != NULL)
    {
        *token = '\0';
    }
    else
    {
        buf[bufLen - 1] = '\0';
        while (token == NULL)
        {
            fgets(bin, GabageBufSize, stdin);
            token = strchr(bin, '\n');
            if (token != NULL)
            {
                break;
            }
        }
    }
    return strlen(buf);
}

//This is a bad implementation
void suspend(pthread_mutex_t* lock)
{
    pthread_mutex_trylock(lock);
//If it has never been locked, it will be locked here
    pthread_mutex_lock(lock);
    pthread_mutex_unlock(lock);
}

void *messageSender(void *lock)
{
    int count = 1;
    while (true)
    {
        consoleWrite(".");
        sleep(1);
        if ((count % 3) == 0)
        {
            //Here, wait until the event occurs.
            suspend((pthread_mutex_t*)lock);
        }
        count++;
    }
}

int main(int argc, char *argv[])
{
    pthread_t tid;
    pthread_mutex_t lock;
    char buf[32];
    pthread_mutex_init(&lock, NULL);
    pthread_create(&tid, NULL, messageSender, &lock);
    while (true)
    {
        input(buf, 32);
        pthread_mutex_unlock(&lock); //Unlock operation is not guaranteed from other threads.
        consoleWrite("unlocked.\n");
    }
    pthread_join(tid, NULL);
}

~~ When data comes into the queue, use the callback and hit pthread_mutex_unlock ~~ Unlocking the ~~ Mutex object restarts the thread. ~~ ~~ In the above demo, the part where the data is stored in the queue is replaced with the input from the keyboard. ~~

~~ I implemented it as long as I could do it without thinking about performance, so ~~ ~~ If you have any other good ideas, please do. ~~ I will write an example using pthread_cond_wait in another post at a later date.


Even more wrong implementation from here

Reference: Section: Man page C Library Functions (3) PTHREAD_COND

  1. pthread_cond_wait takes cond (= condition: condition variable) and mutex as arguments
  2. The mutex object passed to pthread_cond_wait must be locked
  3. Threads stopped by pthread_cond_wait are restarted by pthread_cond_signal
//Correction difference only

typedef struct Suspend
{
    pthread_mutex_t lockMutex;
    pthread_cond_t lockCond;
} Suspend;

void suspend(Suspend *lock)
{
//Wrong usage
    pthread_mutex_trylock(&lock->lockMutex);
    pthread_cond_wait(&lock->lockCond,&lock->lockMutex);
    pthread_mutex_unlock(&lock->lockMutex);
}

void SuspendInit(Suspend *lock)
{
    pthread_cond_init(&lock->lockCond, NULL);
    pthread_mutex_init(&lock->lockMutex, NULL);
}

int main(int argc, char *argv[])
{
    pthread_t tid;
    Suspend lock;
    char buf[32];

    SuspendInit(&lock);
    pthread_create(&tid, NULL, messageSender, &lock);
    while (true)
    {
        input(buf, 32);
        pthread_cond_signal(&lock.lockCond);
        consoleWrite("unlocked.\n");
    }
    pthread_join(tid, NULL);
}

~~ First of all, I feel that I have fixed the points pointed out. ~~

I feel like I was able to fix it ← It was because of my mind. Reimplemented with pipe (renamed from Suspend stuff to EventInfo stuff)

typedef struct EventInfo
{
    int state;
    int read;
    int write;
    pthread_mutex_t mutex;
} EventInfo;

#define EventInfo_WAIT_BUSY 0
#define EventInfo_WAIT_READY 1

//Wait until the event occurs
void EventInfoWait(EventInfo *lock)
{
    uint8_t msg;
    pthread_mutex_lock(&lock->mutex);
    lock->state = EventInfo_WAIT_READY;
    pthread_mutex_unlock(&lock->mutex);
    int r = read(lock->read, &msg, sizeof(uint8_t));
}

//do nothing
void EventInfoRaisingEvent_None(EventInfo *lock)
{
}
//Only notify.
void EventInfoRaisingEvent_Send(EventInfo *lock)
{
    static uint8_t msg = 0xdeadbeef;
    write(lock->write, &msg, sizeof(uint8_t));
}
//Raise an event
void EventInfoRaisingEvent(EventInfo *lock)
{
    static void (*EventInfoWakeupSendMessage[2])(EventInfo * lock) =
        {
            EventInfoWakeupSendMessage_None,
            EventInfoWakeupSendMessage_Send};
    pthread_mutex_lock(&lock->mutex);
    EventInfoWakeupSendMessage[lock->state](lock);
    lock->state = EventInfo_WAIT_BUSY;
    pthread_mutex_unlock(&lock->mutex);
}

void *messageSender(void *lock)
{
    int count = 1;
    while (true)
    {
        consoleWrite(".");
        sleep(1);
        if ((count % 3) == 0)
        {
            //Here, wait until the event occurs.
            EventInfoWait((EventInfo *)lock);
        }
        count++;
    }
}

int EventInfoInit(EventInfo *lock)
{
    int pfd[2];
    int r = pipe(pfd);
    if (r != 0)
    {
        return -1;
    }
    lock->state = EventInfo_WAIT_BUSY;
    fcntl(pfd[1], F_SETFL, O_NONBLOCK);
    lock->read = pfd[0];
    lock->write = pfd[1];
    pthread_mutex_init(&lock->mutex, NULL);
    return 0;
}

int main(int argc, char *argv[])
{
    pthread_t tid;
    EventInfo lock;
    char buf[32];

    EventInfoInit(&lock);
    pthread_create(&tid, NULL, messageSender, &lock);
    while (true)
    {
        input(buf, 32);
        EventInfoRaisingEvent(&lock);
        consoleWrite("unlocked.\n");
    }
    pthread_join(tid, NULL);
    return 0;
}

Fix, maybe this should be okay as it will achieve the original purpose.

Recommended Posts

Suspend Thread on Linux (corrected version at the bottom of the article)
Put the latest version of Python on linux (Debian) on Chromebook
Install the latest version of Git on your Linux server
The story of running the asp.net core 3.1 app on arm64 version Amazon Linux 2
[2020July] Check the UDID of the iPad on Linux
Use the latest version of PyCharm on Ubuntu
At the time of python update on ubuntu
Install the latest version of CMake on Ubuntu 18.04.4 LTS
Open Chrome version of LINE from the command line [Linux]
Check the type and version of your Linux distribution
Announcing the availability of Java 11 LTS on Amazon Linux 2
Notes on the version of CUDA, cuDNN where tensorflow-gpu worked
Commands and files to check the version of CentOS Linux
Get the host name of the host PC with Docker on Linux
Align the version of chromedriver_binary
Install the JDK on Linux
Paste the link on linux
[Latest version] Let the bot speak at regular intervals on discord.py
On Linux, the time stamp of a file is a little past.
How to update the python version of Cloud Shell on GCP
Test the version of the argparse module
Completion of docker command on Linux
pyenv-change the python version of virtualenv
Change the Python version of Homebrew
Install the latest version of Apache httpd 2.4 from source on Cent OS 8
[Java] [Linux] Investigating how the implementation of Java child processes on Linux is realized
I measured the run queue wait time of a process on Linux
Hook to Shared Library on Linux to interrupt the behavior of existing binaries
2019 version: Unauthorized access trend analysis (example of general-purpose server on the cloud)
Folding @ Home on Linux Mint to contribute to the analysis of the new coronavirus
Summarize the titles of Hottentori at the end and look at the present on the Web