mirror of
https://github.com/sogou/workflow.git
synced 2026-10-22 23:39:54 +08:00
7a5a818e66
Co-authored-by: XieHan <XieHan@sogou-inc.com>
177 lines
4.1 KiB
C
177 lines
4.1 KiB
C
/*
|
|
Copyright (c) 2020 Sogou, Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
Author: Xie Han (xiehan@sogou-inc.com)
|
|
*/
|
|
|
|
/*
|
|
* This message queue originates from the project of Sogou C++ Workflow:
|
|
* https://github.com/sogou/workflow
|
|
*
|
|
* The idea of this implementation is quite simple and abvious. When the
|
|
* get_list is not empty, consumer takes a message. Otherwise the consumer
|
|
* wait until put_list is not empty, and swap two lists. This method performs
|
|
* well when the queue is very busy, and the number of consumers is big.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <pthread.h>
|
|
#include "msgqueue.h"
|
|
|
|
struct __msgqueue
|
|
{
|
|
size_t msg_max;
|
|
size_t msg_cnt;
|
|
int linkoff;
|
|
int nonblock;
|
|
void *head1;
|
|
void *head2;
|
|
void **get_head;
|
|
void **put_head;
|
|
void **put_tail;
|
|
pthread_mutex_t get_mutex;
|
|
pthread_mutex_t put_mutex;
|
|
pthread_cond_t get_cond;
|
|
pthread_cond_t put_cond;
|
|
};
|
|
|
|
void msgqueue_set_nonblock(msgqueue_t *queue)
|
|
{
|
|
queue->nonblock = 1;
|
|
pthread_mutex_lock(&queue->put_mutex);
|
|
pthread_cond_signal(&queue->get_cond);
|
|
pthread_cond_broadcast(&queue->put_cond);
|
|
pthread_mutex_unlock(&queue->put_mutex);
|
|
}
|
|
|
|
void msgqueue_set_block(msgqueue_t *queue)
|
|
{
|
|
queue->nonblock = 0;
|
|
}
|
|
|
|
static size_t __msgqueue_swap(msgqueue_t *queue)
|
|
{
|
|
void **get_head = queue->get_head;
|
|
size_t cnt;
|
|
|
|
queue->get_head = queue->put_head;
|
|
pthread_mutex_lock(&queue->put_mutex);
|
|
while (queue->msg_cnt == 0 && !queue->nonblock)
|
|
pthread_cond_wait(&queue->get_cond, &queue->put_mutex);
|
|
|
|
cnt = queue->msg_cnt;
|
|
if (cnt > queue->msg_max - 1)
|
|
pthread_cond_broadcast(&queue->put_cond);
|
|
|
|
queue->put_head = get_head;
|
|
queue->put_tail = get_head;
|
|
queue->msg_cnt = 0;
|
|
pthread_mutex_unlock(&queue->put_mutex);
|
|
return cnt;
|
|
}
|
|
|
|
void msgqueue_put(void *msg, msgqueue_t *queue)
|
|
{
|
|
void **link = (void **)((char *)msg + queue->linkoff);
|
|
|
|
*link = NULL;
|
|
pthread_mutex_lock(&queue->put_mutex);
|
|
while (queue->msg_cnt > queue->msg_max - 1 && !queue->nonblock)
|
|
pthread_cond_wait(&queue->put_cond, &queue->put_mutex);
|
|
|
|
*queue->put_tail = link;
|
|
queue->put_tail = link;
|
|
queue->msg_cnt++;
|
|
pthread_mutex_unlock(&queue->put_mutex);
|
|
pthread_cond_signal(&queue->get_cond);
|
|
}
|
|
|
|
void *msgqueue_get(msgqueue_t *queue)
|
|
{
|
|
void *msg;
|
|
|
|
pthread_mutex_lock(&queue->get_mutex);
|
|
if (*queue->get_head || __msgqueue_swap(queue) > 0)
|
|
{
|
|
msg = (char *)*queue->get_head - queue->linkoff;
|
|
*queue->get_head = *(void **)*queue->get_head;
|
|
}
|
|
else
|
|
{
|
|
msg = NULL;
|
|
errno = ENOENT;
|
|
}
|
|
|
|
pthread_mutex_unlock(&queue->get_mutex);
|
|
return msg;
|
|
}
|
|
|
|
msgqueue_t *msgqueue_create(size_t maxlen, int linkoff)
|
|
{
|
|
msgqueue_t *queue = (msgqueue_t *)malloc(sizeof (msgqueue_t));
|
|
int ret;
|
|
|
|
if (!queue)
|
|
return NULL;
|
|
|
|
ret = pthread_mutex_init(&queue->get_mutex, NULL);
|
|
if (ret == 0)
|
|
{
|
|
ret = pthread_mutex_init(&queue->put_mutex, NULL);
|
|
if (ret == 0)
|
|
{
|
|
ret = pthread_cond_init(&queue->get_cond, NULL);
|
|
if (ret == 0)
|
|
{
|
|
ret = pthread_cond_init(&queue->put_cond, NULL);
|
|
if (ret == 0)
|
|
{
|
|
queue->msg_max = maxlen;
|
|
queue->linkoff = linkoff;
|
|
queue->head1 = NULL;
|
|
queue->head2 = NULL;
|
|
queue->get_head = &queue->head1;
|
|
queue->put_head = &queue->head2;
|
|
queue->put_tail = &queue->head2;
|
|
queue->msg_cnt = 0;
|
|
queue->nonblock = 0;
|
|
return queue;
|
|
}
|
|
|
|
pthread_cond_destroy(&queue->get_cond);
|
|
}
|
|
|
|
pthread_mutex_destroy(&queue->put_mutex);
|
|
}
|
|
|
|
pthread_mutex_destroy(&queue->get_mutex);
|
|
}
|
|
|
|
errno = ret;
|
|
free(queue);
|
|
return NULL;
|
|
}
|
|
|
|
void msgqueue_destroy(msgqueue_t *queue)
|
|
{
|
|
pthread_cond_destroy(&queue->put_cond);
|
|
pthread_cond_destroy(&queue->get_cond);
|
|
pthread_mutex_destroy(&queue->put_mutex);
|
|
pthread_mutex_destroy(&queue->get_mutex);
|
|
free(queue);
|
|
}
|
|
|