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?
Points from @drab
Points to be pointed out by @ angel_p_57
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.
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 **
#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.
Reference: Section: Man page C Library Functions (3) PTHREAD_COND
//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. ~~
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