hanging of sem_wait() - c

I have a some multithreading application. This is a part of main function:
/* ...some code before... */
for(i=0; i<THREADS_COUNT; i++){
status = pthread_create(&threads[i], NULL, thread_main, NULL);
if(status < 0){
fprintf(stderr, "threads error\n");
exit(2);
}
}
status = sem_init(&sem, 0, 0);
if(status < 0){
fprintf(stderr, "sem_init error\n");
exit(4);
}
/* recv loop */
while (1) {
rv = recv(fd, buf, BUFSIZE, 0);
if(rv >= 0){
current = malloc(sizeof(struct queue_msg_list));
/* adding to our local queue */
if(current != NULL){
current->rv = rv;
current->h = h;
memcpy(&(current->buf), &buf, BUFSIZE);
current->next = NULL;
if(main_head == NULL){
main_head = main_tail = current;
}
else {
main_tail->next = current;
main_tail = current;
}
count++;
}
/* if we can carry the local queue to the queue for threads then we are doing it */
if(!pthread_mutex_trylock(&mlock)){
if(thread_head == NULL){
/* if the threads-queue is empty then replace queues */
thread_head = main_head;
thread_tail = main_tail;
} else {
/* if the threads-queue is not empty then add the local queue to the threads-queue */
thread_tail->next = main_head;
thread_tail = main_tail;
}
/* we increasing a semaphore of number of added elements */
for(i=0; i<count; i++){
sem_post(&sem);
printf("sem_post \n");
}
count = 0;
pthread_mutex_unlock(&mlock);
main_head = NULL;
main_tail = NULL;
}
}
}
/* ...some code after... */
And this is a function for threads:
void *thread_main(void *arg)
{
struct queue_msg_list *current;
char buf[BUFSIZE] __attribute__ ((aligned));
struct nfq_handle *h;
int rv;
while(1){
sem_wait(&sem);
pthread_mutex_lock(&mlock);
/* if no blocking then we are working with packet and removing it from list after */
current = thread_head;
rv = current->rv;
h = current->h;
memcpy(&buf, &(current->buf), BUFSIZE);
thread_head = thread_head->next;
pthread_mutex_unlock(&mlock);
nfq_handle_packet(h, buf, rv);
free(current);
}
}
This application always works true on PC. I have put this application to some router (linux kernel version in firmware is 2.6.30). It works correctly sometimes but sometimes it works incorrectly. :)
Threads hang on calling sem_wait(&sem); sometimes but the semaphore value is above zero (1, 2, 3, etc). Why?
P.S. I tried to check the return value of sem_wait(&sem); but did not get it.

Related

VFIO interrupts using eventfd: can eventfd semaphore behaviour be maintained?

I have a program running on a QEMU VM. The program running inside this VM gets notified by a program on the host via interrupts and using QEMU ivshmem. The program on the host creates an eventfd and sends this file descriptor to QEMU when the VM starts. The program in the guest then opens a VFIO group device and sets an interrupt request fd on this device. We can then add the interrupt fd to epoll and epoll_wait to wait for notifications from the host.
The thing is that I want a 1-1 matching between the times the host writes to the eventfd and the number of events that are signaled in epoll_wait. For this I decided to use EFD_SEMAPHORE for the evenfds on the host and the guest. From my understanding, every time I write an 8 byte integer with value 1, the eventfd_counter is incremented by 1. Then every time the eventfd is read, the counter is decremented by 1 (different from a regular eventfd where each read clears the whole counter). For some reason, I am not getting the desired behaviour, so I was wondering if either eventfds with the EFD_SEMAPHORE flags are not properly supported by VFIO or QEMUs ivshmem.
Below is a simplified version of the parts I think are relevant and how I setup the notification system. I hope the code below is not too verbose. I tried to reduce the number of irrelevant parts (there is too much other code in the middle that is not particularly relevant to the problem) but not 100% sure what might be relevant or not.
Code host uses to signal guest
int ivshmem_uxsocket_send_int(int fd, int64_t i)
{
int n;
struct iovec iov = {
.iov_base = &i,
.iov_len = sizeof(i),
};
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = NULL,
.msg_controllen = 0,
.msg_flags = 0,
};
if ((n = sendmsg(fd, &msg, 0)) != sizeof(int64_t))
{
return -1;
}
return n;
}
int ivshmem_uxsocket_sendfd(int uxfd, int fd, int64_t i)
{
int n;
struct cmsghdr *chdr;
/* Need to pass at least one byte of data to send control data */
struct iovec iov = {
.iov_base = &i,
.iov_len = sizeof(i),
};
/* Allocate a char array but use a union to ensure that it
is aligned properly */
union {
char buf[CMSG_SPACE(sizeof(fd))];
struct cmsghdr align;
} cmsg;
memset(&cmsg, 0, sizeof(cmsg));
/* Add control data (file descriptor) to msg */
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = &cmsg,
.msg_controllen = sizeof(cmsg),
.msg_flags = 0,
};
/* Set message header to describe ancillary data */
chdr = CMSG_FIRSTHDR(&msg);
chdr->cmsg_level = SOL_SOCKET;
chdr->cmsg_type = SCM_RIGHTS;
chdr->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(chdr), &fd, sizeof(fd));
if ((n = sendmsg(uxfd, &msg, 0)) != sizeof(i))
{
return -1;
}
return n;
}
/* SETUP IVSHMEM WITH QEMU AND PASS THE EVENTFD USED TO
NOTIFY THE GUEST */
int ivshmem_uxsocket_accept()
{
int ret;
int cfd, ifd, nfd;
int64_t version = IVSHMEM_PROTOCOL_VERSION;
uint64_t hostid = HOST_PEERID;
int vmid = 0
/* Accept connection from qemu ivshmem */
if ((cfd = accept(uxfd, NULL, NULL)) < 0)
{
return -1;
}
/* Send protocol version as required by qemu ivshmem */
ret = ivshmem_uxsocket_send_int(cfd, version);
if (ret < 0)
{
return -1;
}
/* Send vm id to qemu */
ret = ivshmem_uxsocket_send_int(cfd, vmid);
if (ret < 0)
{
return -1;
}
/* Send shared memory fd to qemu */
ret = ivshmem_uxsocket_sendfd(cfd, shm_fd, -1);
if (ret < 0)
{
return -1;
}
/* Eventfd used by guest to notify host */
if ((nfd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK)) < 0)
{
return -1;
}
/* Ivshmem protocol requires to send host id
with the notify fd */
ret = ivshmem_uxsocket_sendfd(cfd, nfd, hostid);
if (ret < 0)
{
return -1;
}
/* THIS IS THE EVENTFD OF INTEREST TO US: USED BY HOST
TO NOTIFY GUEST */
if ((ifd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK)) < 0)
{
return -1;
}
ret = ivshmem_uxsocket_sendfd(cfd, ifd, vmid);
if (ret < 0)
{
return -1;
}
if (epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev) < 0)
{
return -1;
}
return 0;
}
/* NOW EVERY TIME WE WANT TO NOTIFY THE GUEST
WE CALL THE FOLLOWING FUNCTION */
int notify_guest(int fd)
{
int ret;
uint64_t buf = 1;
ret = write(fd, &buf, sizeof(uint64_t));
if (ret < sizeof(uint64_t))
{
return -1;
}
return 0;
}
Code guest uses to receive notifications from host
/* THIS FUNCTION SETS THE IRQ THAT RECEIVES THE
NOTIFICATIONS FROM THE HOST */
int vfio_set_irq(int dev)
{
int fd;
struct vfio_irq_set *irq_set;
char buf[sizeof(struct vfio_irq_set) + sizeof(int)];
if ((fd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK)) < 0)
{
return -1;
}
irq_set = (struct vfio_irq_set *) buf;
irq_set->argsz = sizeof(buf);
irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
irq_set->index = 2;
irq_set->start = 0;
irq_set->count = 1;
memcpy(&irq_set->data, &fd, sizeof(int));
if (ioctl(dev, VFIO_DEVICE_SET_IRQS, irq_set) < 0)
{
return -1;
}
return irq_fd;
}
/* The guest sets up the ivshmem region from QEMU and sets the
interrupt request. */
int vfio_init()
{
int cont, group, irq_fd;
struct epoll_event ev;
struct vfio_group_status g_status = { .argsz = sizeof(g_status) };
struct vfio_device_info device_info = { .argsz = sizeof(device_info) };
/* Create vfio container */
if ((cont = open("/dev/vfio/vfio", O_RDWR)) < 0)
{
return -1;
}
/* Check API version of container */
if (ioctl(cont, VFIO_GET_API_VERSION) != VFIO_API_VERSION)
{
return -1;
}
if (!ioctl(cont, VFIO_CHECK_EXTENSION, VFIO_NOIOMMU_IOMMU))
{
return -1;
}
/* Open the vfio group */
if((group = open(VFIO_GROUP, O_RDWR)) < 0)
{
return -1;
}
/* Test if group is viable and available */
ioctl(group, VFIO_GROUP_GET_STATUS, &g_status);
if (!(g_status.flags & VFIO_GROUP_FLAGS_VIABLE))
{
return -1;
}
/* Add group to container */
if (ioctl(group, VFIO_GROUP_SET_CONTAINER, &cont) < 0)
{
return -1;
}
/* Enable desired IOMMU model */
if (ioctl(cont, VFIO_SET_IOMMU, VFIO_NOIOMMU_IOMMU) < 0)
{
return -1;
}
/* Get file descriptor for device */
if ((dev = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, VFIO_PCI_DEV)) < 0)
{
return -1;
}
/* Get device info */
if (ioctl(dev, VFIO_DEVICE_GET_INFO, &device_info) < 0)
{
return -1;
}
/* Set interrupt request fd */
if ((irq_fd = vfio_set_irq(dev)) < 0)
{
return -1
}
/* Add interrupt request fd to interest list */
if (vfio_subscribe_irq() < 0)
{
return -1;
}
/* Do other shm setup stuff not related to the interrupt
request */
ev.events = EPOLLIN;
ev.data.ptr = EP_NOTIFY;
ev.data.fd = irq_fd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, irq_fd, &ev) != 0)
{
return -1;
}
return 0;
}
int ivshmem_drain_evfd(int fd)
{
int ret;
uint64_t buf;
ret = read(fd, &buf, sizeof(uint64_t));
if (ret == 0)
{
return -1;
}
return ret;
}
/* I should get every notification from the host here,
but it seems that not all notifications are going
through. The number of calls to notify_guest does not
match the number of events received from epoll_wait
here */
int notify_poll()
{
int i, n;
struct epoll_event evs[32];
n = epoll_wait(epfd, evs, 32, 0);
for (i = 0; i < n; i++)
{
if (evs[i].events & EPOLLIN)
{
/* Drain evfd */
drain_evfd(irq_fd);
/* Handle notification ... */
handle();
}
}
}

Custom Shell Seg Faulting

I'm trying to make a custom shell for a class assignment. The TLDR is... I don't really know what's wrong with it. From the debugging, it looks like there's an error in the loop() function, but I don't know why, or where the error exists.
What's happening is the application executes the command correctly. However, on the next loop for the next command input, it segfaults. I've tried to make sure all the variables are freed so I'm not writing attempting to write into memory that I can't. That's the major issue. The second (much more minor) issue is that I can't get piping to work. I'm also not sure why. I thought I re-wrote stdin/stdout correctly. (Yes, I know strtok id deprecated. I do plan on moving to "strtok_r")
Does anyone have guidance or suggestions on what I'm not doing correctly?
Cheers,
M
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <limits.h>
#define TOK_DELIM " \t\r\n\a\v" // \a is Alert. \v is vertical tab.
#define FG_EXEC 0
#define BG_EXEC 1
#define MAXARG 20
#define CMD_MAX 100 //Some arbitrary value...
/* Set up Command Segements */
struct cmd_seg {
char *args[MAXARG];
struct cmd_seg *next; // Linked List. Next node
};
struct cmd {
struct cmd_seg *root;
int mode; //BG, FG
};
/* Helper Functions - Core Functionality */
void cd(char *path){
int code = chdir(path); //May only return -1 (error) or 0 (success)
if(code == -1){
printf("\nFailed to change your path to \"%s\"\n", path);
}
else{
printf("\nChanged path to: %s\n", path);
}
}
/* Helper Functions - Job Control */
void bg(pid_t pid){
int status;
if(kill(pid, SIGCONT) < 0){ printf("\nBG: JNF"); }
else{ waitpid(pid, &status, WUNTRACED); }
}
void fg(pid_t pid){
setpgid(pid, pid);
int status;
if (tcsetpgrp(1, getpgid(pid)) != 0){
printf("\nFG: JNF");
}
else{
kill(pid, SIGCONT);
waitpid(pid, &status, WUNTRACED);
}
}
/* Helper Functions - PID, Child, Forks, etc */
void kill_child(pid_t pid){ // What an unforunate name... Dark humour.
kill(pid, SIGTERM); //Safe Termination. SIGKILL may ne required?
}
/*
void destroyList(struct cmd** cmd1){
struct cmd* cmd_cur = cmd1;
struct cmd_seg* cs_cur;
struct cmd_seg* next;
cs_cur = cur->root;
while(cs_cur != NULL){
next = cs_cur->next;
free(cs_cur);
cs_cur = next;
}
free(cs_cur);
free(cmd1);
}
*/
/* Helper Functions - Check for Built-in Command */
int builtins(struct cmd_seg *segment){
/* Check for Internal Commands - CD, Exit, etc */
if(strcmp(segment->args[0], "cd") == 0){
cd(segment->args[1]);
char path[PATH_MAX];
getcwd(path, sizeof(path));
printf("%s%%", path);
fflush(stdout);
return 1;
}
else if(strcmp(segment->args[0], "exit") == 0){
printf("\nExiting Core Application.\n\n");
exit(0);
}
/*
else if(strcmp(segment->args[0], "kill") == 0){
pid_t pid;
pid = atoi(segment->args[1]);
if(pid!=0){ // if 0, implies it's not an integer, failed extraction;
kill_child(pid);
}
return 1;
}
*/
else{
return -1;
}
}
int exec_cmd_seg(struct cmd_seg *segment, int in_fd, int out_fd, int mode, int pGrpID){
int status = 1;
int IsBuiltin = builtins(segment);
if(IsBuiltin == 0){ return -1; }
else if(IsBuiltin == 1){ return 1; }
/*
* Not a Builtin Command, Application has not Exited
* Therefore fork
*/
pid_t child_pid;
child_pid = fork();
if(child_pid < 0){
printf("\nForking failed - Exiting Gracefully");
exit(0);
}
else if(child_pid == 0){
/* Forked */
int damnPIDs = getpid();
if(mode == FG_EXEC){ printf("\nFG Execution # PID %d", damnPIDs); }
else{ printf("\nBG Execution # PID %d", damnPIDs); }
signal(SIGINT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
signal(SIGCONT, SIG_DFL);
/* Re-write In-Outs */
dup2(in_fd, 0);
dup2(out_fd, 1);
if(in_fd != 0){
close(in_fd);
}
else if(out_fd != 1){
close(out_fd);
}
/* Check args[1] */
int result = execvp(segment->args[0], segment->args);
if(result < 0){
printf("\nError Executing Command: %s", segment->args[0]);
//exit(0);
}
}
else{
signal(SIGINT, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGINT, SIG_DFL);
if (mode == BG_EXEC)
signal(SIGCHLD, SIG_IGN);
else
waitpid(child_pid, &status, WUNTRACED);
if(in_fd != 0){
close(in_fd);
}
if(out_fd != 1){
close(out_fd);
}
return 1;
}
return 1;
}
int exec_cmd(struct cmd *command){
int status = 1;
struct cmd_seg* cur;
struct cmd_seg* pfree;
int fd = 0;
//int count = 0;
//printf("\nEXEC_CMD: Entering For1");
//fflush(stdout);
for(cur = command->root; cur != NULL; cur = cur -> next){
//printf("\nExec_CMD - Command %d: %s", count, command->root->args);
//fflush(stdout);
if(cur -> next){
//printf("\nExec_CMD - Command %d: %s", count, cur->args);
//fflush(stdout);
int fd2[2];
pipe(fd2);
status = exec_cmd_seg(cur, fd, fd2[1], command->mode, 0);
fd = fd2[0];
}
else{
status = exec_cmd_seg(cur, fd, 1, command->mode, 0);
}
cur = command -> root;
pfree = cur;
while(cur != NULL){
cur = cur -> next;
/* Clear pfree before setting it */
free(pfree);
pfree = cur;
}
}
cur = NULL;
pfree = NULL;
free(command);
command = NULL;
return status;
}
/* Fix to use strtok_r */
struct cmd* parser(char *line){
struct cmd* command = (struct cmd*)malloc(sizeof(struct cmd));
command->root = (struct cmd_seg*)malloc(sizeof(struct cmd_seg));
struct cmd_seg* cur;
struct cmd_seg* pnew;
cur = command->root;
/* Check if it is background command */
char* pStart = line; //pointer
int count = 0;
while ((*pStart != '\n') && (*pStart != '\0')) {
if (*pStart == '&') {
count = 1;
*pStart = '\0';
break;
}
pStart++;
}
command->mode = count;
/* Parse line as command Link list */
char *res = line;
char *temp;
int i = 0;
temp = strsep(&res, "|");
for (i = 0; i < MAXARG - 1 && (cur->args[i] = strtok(temp, TOK_DELIM)) != NULL; i++)
temp = NULL;
cur->args[i] = NULL;
while ((temp = strsep(&res, "|")) != NULL) {
pnew = (struct cmd_seg*) malloc(sizeof(struct cmd_seg));
cur->next = pnew;
cur = pnew;
for (i = 0; i < MAXARG - 1 && (cur->args[i] = strtok(temp, TOK_DELIM)) != NULL; i++)
temp = NULL;
cur->args[i] = NULL;
}
cur->next = NULL;
//free(cur);
//free(pnew);
//free(pStart);
return command;
}
char* readLine(){
char *buffer = malloc(sizeof(char)*CMD_MAX);
char ch;
int pos = 0;
if(buffer){
//printf("Inside Buffer If");
//fflush(stdout);
while(1){
ch = getchar();
//printf("Inside While\n");
//fflush(stdout);
/* Buffer Allocated */
if(ch == EOF || ch == '\n'){
printf("Command: %s\n", buffer);
return buffer;
}
else{
buffer[pos] = ch;
}
pos++;
}
}
else{
printf("Allocation Failed\n");
fflush(stdout);
exit(0);
}
}
void loop(){
/* Loop Command Input */
int status = 1;
//int l_count = 0;
while(status >= 0){
char *line;
struct cmd *cmd;
//printf("Inside Loop - 1\n");
//fflush(stdout);
/* Print Shell Prompt */
char path[PATH_MAX];
getcwd(path, sizeof(path));
printf("%s%%", path);
fflush(stdout);
line = readLine();
if(strlen(line) == 0){
printf("Failed to read line.");
exit(0);
}
else{
cmd = parser(line);
if(cmd!=NULL){
//printf("\nCMD not null - executing");
//fflush(stdout);
status = exec_cmd(cmd);
}
else{
printf("\nFailed to parse command correctly");
fflush(stdout);
exit(0);
}
}
free(line);
//free(cmd);
//free(path); Not a heap device -- GCC Output
}
}
int main(int argc, char **argv){
loop();
return 1;
}
The segfault occurs in the for statement. The body of the for-loop has executed one time, then the loop-expression cur = cur->next is executed, but cur is NULL.
It's NULL because of some code in the body of the for-loop which frees all the cmd_segs in the linked list (I suspect that code is supposed to be after the for-loop rather than inside it).
int exec_cmd(struct cmd *command){
int status = 1;
struct cmd_seg* cur;
struct cmd_seg* pfree;
for(cur = command->root; cur != NULL; cur = cur -> next){
if(cur -> next){
...
}
cur = command -> root;
pfree = cur;
while(cur != NULL){
cur = cur -> next;
free(pfree);
pfree = cur;
}
}
...
}
The cond-expression cur != NULL doesn't catch this because it is executed after the init-expression and then after each loop-expression.
After I fixed this by moving the cmd_seg-freeing code to be after the for-loop, I looked at pipeline commands, but they all succeeded for me.

Trouble with converting single threaded HTTP server into multithreaded using pthreads

My single threaded HTTP Server works just fine, but I'm having trouble multithreading it. I know I am supposed to use pthreads, locks, and condition variables, but I can't get the logic set up properly. The trouble starts after listening to the server. Currently I have a struct that contains a client socket variable, a lock variable, a condition variable, and some variables necessary for parsing and storing headers. I create a struct array sized with the amount of threads, then create a pthread array sized with the amount of threads. I go into a while(1) loop which goes into a for loop and iterates through all the threads accepting each connection, calling pthread_create and passing them to my handle connections function, then closing the client socket. My handle connections then does the request handling that my single threaded http server did (reading, parsing, processing, constructing), then returns NULL. No request gets read when I run this using pthread_create, but if I run handle connections without the pthreads, it works just fine. And below I'll attach my code. Any help is appreciated
Thank you for commenting so well ...
Okay, I coded up, but not tested the changes.
Your loop is inherently single threaded, so a bit of refactoring is in order
You have to scan for an unused thread control slot after doing accept.
You have to pthread_join completed/done threads [from any prior invocations].
The thread function has to close the per-client socket [not main thread]
You need a global (file scope) mutex.
I've coded it up, but not tested it. I put #if 0 around most of what I clipped out and #if 1 around new code.
Note that number of simultaneous connections [second arg to listen], herein 5 has to be less than or equal to threadNum. Although I didn't do it, I'd just do listen(...,threadNum) instead of hardwiring it.
Here's the short code with just the relevant changes:
#if 1
pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
struct threadObject {
char method[5]; // PUT, HEAD, GET. HEAD==4 letters+null terminator
char filename[28]; // what is the file we are worried about. Max 27 ASCII characters (NULL terminated on 28)
char httpversion[9]; // HTTP/1.1
ssize_t content_length; // example: 13
uint16_t status_code; // status code for the request
char buffer[BUFFER_SIZE]; // buffer to transfer data
char rest_of_PUT[BUFFER_SIZE]; // incase client send part of PUT message in header
int client_sockd;
pthread_mutex_t *dispatch_lock;
const pthread_cond_t *job_pool_empty;
// pthread_mutex_t* log_lock;
// const pthread_cond_t* log_pool_empty;
pthread_mutex_t *read_write_lock;
pthread_cond_t *file_list_update;
// JobQueue* job_pool;
// LogQueue log_pool;
// bool is_logging;
#if 1
pthread_t tsk_threadid;
int tsk_inuse;
int tsk_done;
#endif
};
void *
handle_connections(void *ptr_thread)
{
// create a mutual exclusion to lock out any other threads from the function
// pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// pthread_mutex_lock(&mutex);
// operations go here
struct threadObject *thread = (struct threadObject *) ptr_thread;
// reset message after each loop
memset(thread->buffer, '\0', BUFFER_SIZE);
memset(thread->method, '\0', 5);
memset(thread->filename, '\0', 28);
memset(thread->httpversion, '\0', 9);
thread->content_length = 0;
thread->status_code = 0;
memset(thread->rest_of_PUT, '\0', BUFFER_SIZE);
// read message
if (read_http_response(thread) == true) {
// process message
process_request(thread);
}
// construct a response
construct_http_response(thread);
// unlock the function
// pthread_mutex_unlock(&mutex);
#if 1
close(thread->client_sockd);
pthread_mutex_lock(&global_mutex);
thread->tsk_done = 1;
pthread_mutex_unlock(&global_mutex);
#endif
return NULL;
}
int
main(int argc, char **argv)
{
// Create sockaddr_in with server information
if (argc < 2) {
perror("No arguments passed\n");
return -1;
}
// make sure port number is above 1024 and set the port # to it
if (atoi(argv[1]) < 1024) {
return 1;
}
char *port = argv[1];
// parse the command line args for options -l and -N. -l specifies it will use a log and the following parameter is the filename. -N specifies the number of threads it will use and the following parameter will be a number
int opt;
uint8_t threadNum = 1;
char *logName = NULL;
while ((opt = getopt(argc - 1, argv + 1, "N:l:")) != -1) {
if (opt == 'N') {
threadNum = atoi(optarg);
}
else if (opt == 'l') {
logName = optarg;
}
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(port));
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t addrlen = sizeof(server_addr);
// Create server socket
int server_sockd = socket(AF_INET, SOCK_STREAM, 0);
// Need to check if server_sockd < 0, meaning an error
if (server_sockd < 0) {
perror("socket");
return 1;
}
// Configure server socket
int enable = 1;
// This allows you to avoid: 'Bind: Address Already in Use' error
int ret = setsockopt(server_sockd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
if (ret < 0) {
return EXIT_FAILURE;
}
// Bind server address to socket that is open
ret = bind(server_sockd, (struct sockaddr *) &server_addr, addrlen);
if (ret < 0) {
return EXIT_FAILURE;
}
// Listen for incoming connections
ret = listen(server_sockd, 5); // 5 should be enough, if not use SOMAXCONN
if (ret < 0) {
return EXIT_FAILURE;
}
struct threadObject thread[threadNum];
// Connecting with a client
struct sockaddr client_addr;
socklen_t client_addrlen = sizeof(client_addr);
// create a pthread array of size (number of threads). specify this will be using the handle connections function. join the threads together
#if 0
pthread_t thread_id[threadNum];
#endif
#if 1
struct threadObject *tsk = NULL;
int tskidx;
// clear out the thread structs
for (tskidx = 0; tskidx < threadNum; tskidx++) {
tsk = &thread[tskidx];
memset(tsk,0,sizeof(struct threadObject));
}
while (true) {
// accept connection
int client_sockd = accept(server_sockd, &client_addr, &client_addrlen);
pthread_mutex_lock(&global_mutex);
// join any previously completed threads
for (tskidx = 0; tskidx < threadNum; tskidx++) {
tsk = &thread[tskidx];
if (tsk->tsk_done) {
pthread_join(tsk->tsk_threadid,NULL);
tsk->tsk_inuse = 0;
tsk->tsk_done = 0;
}
}
// find unused task slot
for (tskidx = 0; tskidx < threadNum; tskidx++) {
tsk = &thread[tskidx];
if (! tsk->tsk_inuse)
break;
}
memset(tsk,0,sizeof(struct threadObject));
tsk->client_sockd = client_sockd;
tsk->tsk_inuse = 1;
pthread_mutex_unlock(&global_mutex);
// fire in the hole ...
pthread_create(&tsk->tsk_threadid, NULL, handle_connections, tsk);
}
#endif
#if 0
for (int i = 0; i < threadNum; i++) {
printf("\n[+] server is waiting...\n");
thread[i].client_sockd = accept(server_sockd, &client_addr, &client_addrlen);
handle_connections(&thread[i]);
// pthread_create(&thread_id[i], NULL, handle_connections, &thread[i]);
printf("Response Sent\n");
// close the current client socket
close(thread[i].client_sockd);
}
}
#endif
return EXIT_SUCCESS;
}
Here's the complete code [just in case I clipped out too much]:
#include <sys/socket.h>
#include <sys/stat.h>
#include <stdio.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <fcntl.h>
#include <unistd.h> // write
#include <string.h> // memset
#include <stdlib.h> // atoi
#include <stdbool.h> // true, false
#include <errno.h>
#include <sys/types.h>
#include <ctype.h>
#include <pthread.h>
#define BUFFER_SIZE 4096
#if 1
pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
struct threadObject {
char method[5]; // PUT, HEAD, GET. HEAD==4 letters+null terminator
char filename[28]; // what is the file we are worried about. Max 27 ASCII characters (NULL terminated on 28)
char httpversion[9]; // HTTP/1.1
ssize_t content_length; // example: 13
uint16_t status_code; // status code for the request
char buffer[BUFFER_SIZE]; // buffer to transfer data
char rest_of_PUT[BUFFER_SIZE]; // incase client send part of PUT message in header
int client_sockd;
pthread_mutex_t *dispatch_lock;
const pthread_cond_t *job_pool_empty;
// pthread_mutex_t* log_lock;
// const pthread_cond_t* log_pool_empty;
pthread_mutex_t *read_write_lock;
pthread_cond_t *file_list_update;
// JobQueue* job_pool;
// LogQueue log_pool;
// bool is_logging;
#if 1
pthread_t tsk_threadid;
int tsk_inuse;
int tsk_done;
#endif
};
//read in the header and store it in the appropriate places
bool
read_http_response(struct threadObject *thread)
{
printf("\nThis function will take care of reading message\n");
// how many bytes we're receiving from the header. also puts the message into the buffer
ssize_t bytes = recv(thread->client_sockd, thread->buffer, BUFFER_SIZE, 0);
// if nothing or too much gets sent in the header, return
if (bytes <= 0 || bytes >= BUFFER_SIZE) {
thread->status_code = 400;
printf("Too long or nothing in here\n");
return false;
}
// NULL terminate the last spot on the buffer
thread->buffer[bytes] = '\0';
// how many bytes we received
printf("[+] received %ld bytes from client\n[+] response: \n", bytes);
printf("those bytes are: %s\n", thread->buffer);
// make a char pointer pointer to the buffer to easily traverse it and parse it into the right spots
char *traverse = thread->buffer;
// first stop. sgnals the beginning of the filename
char *file = strstr(traverse, "/");
// 2nd stop. signls the beginning of the HTTP version. only 1.1 is accepted
char *http = strstr(traverse, "HTTP/1.1");
// 3rd stop. Signals the beginning of the content length
char *contlength1 = strstr(traverse, "Content-Length");
char *chunked = strstr(traverse, "chunked");
if (chunked != NULL) {
printf("MESSAGE NOT A FILE PUT\n");
thread->status_code = 403;
return false;
}
// store the method
sscanf(traverse, "%s", thread->method);
printf("method:%s\n", thread->method);
// if its not 1 of the 3 valid requests, throw 400 error
if (strcmp(thread->method, "GET") != 0 &&
strcmp(thread->method, "PUT") != 0 &&
strcmp(thread->method, "HEAD") != 0) {
thread->status_code = 400;
printf("Invalid Method:%s\n", thread->method);
return false;
}
// if the filename doesnt start with /, its invalid throw 400 error
if (*file != '/') {
thread->status_code = 400;
printf("bad filename\n");
return false;
}
// only store the filename portion after the required /
traverse = file + 1;
// to make sure the filename isnt too long
uint8_t size_check = 0;
// traverse filename until first whitespace
while (*traverse != ' ') {
// if any character in the filename isnt 1 of these, its invalid. throw 400 error
if (!isalnum(*traverse) && *traverse != '_' && *traverse != '-') {
// if theres no filename at all, throw a 404 error
if (size_check == 0) {
thread->status_code = 404;
printf("No file specified\n");
return thread->status_code;
}
thread->status_code = 400;
printf("Invalid filename character:%c\n", *traverse);
return false;
}
sscanf(traverse++, "%c", thread->filename + size_check++);
// if the filename breaks the 27 character limit, return a 400 error
if (size_check > 27) {
thread->status_code = 400;
printf("filename too long\n");
return false;
}
}
printf("filename:%s\n", thread->filename);
// if HTTP/1.1 isnt given, throw a 400 error
if (http == NULL) {
printf("HTTP/1.1 400 Bad Request\r\n\r\n");
thread->status_code = 400;
return false;
}
traverse = http;
// read in the http version until the first \r\n. this signals the end of the given version name
sscanf(traverse, "%[^\r\n]s", thread->httpversion);
printf("HTTP:%s\n", thread->httpversion);
// if its not a put request, this is the end of the header. return
if (strcmp(thread->method, "PUT") != 0) {
return true;
}
// for put requests only. traverse until the beginning of the content length
traverse = contlength1;
// last stop. signals the end of a normal PUT header. if a client wants to put some of the message in the header, it gets stored after this
char *end = strstr(traverse, "\r\n\r\n");
// if theres no \r\n\r\n, the header is bad. return 400
if (end == NULL) {
printf("bad header\n");
thread->status_code = 400;
return false;
}
// traverse to the next digit
while (!isdigit(*traverse)) {
// if theres no next digit after "content length", the header is bad. return 400
if (traverse == end) {
printf("bad header\n");
thread->status_code = 400;
return false;
}
traverse++;
}
// set to traverse to be sure fit the entire content length. use size_check to traverse through
char *temp = traverse;
size_check = 0;
// while its taking in digits, put them into the char array.
while (isdigit(*traverse)) {
sscanf(traverse++, "%c", temp + size_check++);
}
// convert the new string into numbers
thread->content_length = atoi(temp);
// if the content length is < 0 throw a 400 error
if (thread->content_length < 0) {
thread->status_code = 400;
printf("bad content length:%ld\n", thread->content_length);
return false;
}
// printf("Content Length:%ld\n", thread->content_length);
// move +4 spots to get to the end of this. if its a normal PUT, this will be the last spot. If the client puts part of the message in the header, it goes after this
traverse = end + 4;
// put the rest of the header into a char array to append later. if theres nothing, itll do nothing
strcpy(thread->rest_of_PUT, traverse);
// printf("Rest of PUT:%s\n", thread->rest_of_PUT);
// will only get here if status code is 0
return true;
}
//process the message we just recieved
void
process_request(struct threadObject *thread)
{
printf("\nProcessing Request\n");
// server side file descriptor
int fd;
// if the method is PUT
if (strcmp(thread->method, "PUT") == 0) {
// open the file for read only to check if its already there or not to set proper status code
fd = open(thread->filename, O_WRONLY);
// if it doesnt exist, set 201 status code
struct stat checkExist;
if (stat(thread->filename, &checkExist) != 0) {
thread->status_code = 201;
}
// if it exists, set 200 and overwrite
else {
struct stat fileStat;
fstat(fd, &fileStat);
// check write permission
if ((S_IWUSR & fileStat.st_mode) == 0) {
printf("MESSAGE NOT WRITEABLE PUT\n");
thread->status_code = 403;
return;
}
thread->status_code = 200;
}
// close it
close(fd);
// reopen it. this time for writing to or overwriting. if its there, overwrite it. if not, create it. cant use for status codes since it will always create a new file
fd = open(thread->filename, O_WRONLY | O_CREAT | O_TRUNC);
// printf("fd in process is:%d\n", fd);
// if theres a bad fd, throw a 403
if (fd < 0) {
printf("ERROR\n\n");
thread->status_code = 403;
return;
}
// to check that the amount of bytes sent = the amount received
ssize_t bytes_recv,
bytes_send;
// if theres no body, put an empty file on the server
if (thread->content_length == 0) {
bytes_send = write(fd, '\0', 0);
}
// if there is a body, put it onto the new file created on the server and make sure the received bytes = the sent ones
else {
ssize_t total = 0,
len_track = thread->content_length;
while (thread->content_length != 0) {
bytes_recv = recv(thread->client_sockd, thread->buffer, BUFFER_SIZE, 0);
bytes_send = write(fd, thread->buffer, bytes_recv);
total += bytes_send;
// if the received bytes != the sent byes, send a 500 error
if (bytes_recv != bytes_send) {
thread->status_code = 500;
printf("Recieved != sent for put request\n");
return;
}
thread->content_length -= bytes_recv;
// printf("Bytes read:%ld\nBytes sent:%ld\nMessage content length:%ld\n", bytes_recv, bytes_send, message->content_length);
}
// if the content length != bytes sent, throw a 403 error
if (len_track != total) {
thread->status_code = 403;
printf("Content length != sent for put request\n");
return;
}
}
printf("Message status code:%d\n", thread->status_code);
// close the fd
close(fd);
return;
}
// if the method is GET or HEAD
else if (strcmp(thread->method, "GET") == 0 || strcmp(thread->method, "HEAD") == 0) {
// open the file for reading only
fd = open(thread->filename, O_RDONLY);
// if bad fd, throw a 404
struct stat fileStat;
fstat(fd, &fileStat);
// check read permission and if it exists
if (((S_IRUSR & fileStat.st_mode) == 0) || stat(thread->filename, &fileStat) != 0) {
printf("BAD GET\n");
thread->status_code = 404;
return;
}
else {
thread->status_code = 200;
thread->content_length = lseek(fd, 0, SEEK_END);
}
// close the fd
close(fd);
return;
}
}
void
construct_http_response(struct threadObject *thread)
{
printf("Constructing Response\n");
// size 22 since the largest code is 21 characters + NULL
char response[22];
// 200=OK, 201=CREATED, 400=BAD REQUEST, 403=FORBIDDEN, 404=NOT FOUND, 500=INTERNAL SERVER ERROR
if (thread->status_code == 200) {
strcpy(response, "OK");
}
else if (thread->status_code == 201) {
strcpy(response, "CREATED");
}
else if (thread->status_code == 400) {
strcpy(response, "BAD REQUEST");
}
else if (thread->status_code == 403) {
strcpy(response, "FORBIDDEN");
}
else if (thread->status_code == 404) {
strcpy(response, "NOT FOUND");
}
else if (thread->status_code == 500) {
strcpy(response, "INTERNAL SERVER ERROR");
}
else {
printf("Bad response...\n");
return;
}
dprintf(thread->client_sockd, "%s %d %s\r\nContent-Length: %ld\r\n\r\n", thread->httpversion, thread->status_code, response, thread->content_length);
if (strcmp(thread->method, "GET") == 0 && thread->status_code == 200) {
int fd = open(thread->filename, O_RDONLY);
ssize_t total = 0,
len_track = thread->content_length,
bytes_recv,
bytes_send;
while (thread->content_length != 0) {
bytes_recv = read(fd, thread->buffer, BUFFER_SIZE);
bytes_send = send(thread->client_sockd, thread->buffer, bytes_recv, 0);
if (bytes_recv != bytes_send) {
thread->status_code = 500;
close(fd);
printf("Recieved != sent for GET request\nReceived:%ld\nSent:%ld\n", bytes_recv, bytes_send);
dprintf(thread->client_sockd, "%s %d %s\r\nContent-Length: %ld\r\n\r\n", thread->httpversion, thread->status_code, response, thread->content_length);
close(fd);
return;
}
total += bytes_send;
thread->content_length -= bytes_recv;
}
if (total != len_track) {
thread->status_code = 403;
printf("Content length != recvd for GET request\n");
dprintf(thread->client_sockd, "%s %d %s\r\nContent-Length: %ld\r\n\r\n", thread->httpversion, thread->status_code, response, thread->content_length);
close(fd);
return;
}
close(fd);
}
}
void *
handle_connections(void *ptr_thread)
{
// create a mutual exclusion to lock out any other threads from the function
// pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// pthread_mutex_lock(&mutex);
// operations go here
struct threadObject *thread = (struct threadObject *) ptr_thread;
// reset message after each loop
memset(thread->buffer, '\0', BUFFER_SIZE);
memset(thread->method, '\0', 5);
memset(thread->filename, '\0', 28);
memset(thread->httpversion, '\0', 9);
thread->content_length = 0;
thread->status_code = 0;
memset(thread->rest_of_PUT, '\0', BUFFER_SIZE);
// read message
if (read_http_response(thread) == true) {
// process message
process_request(thread);
}
// construct a response
construct_http_response(thread);
// unlock the function
// pthread_mutex_unlock(&mutex);
#if 1
close(thread->client_sockd);
pthread_mutex_lock(&global_mutex);
thread->tsk_done = 1;
pthread_mutex_unlock(&global_mutex);
#endif
return NULL;
}
int
main(int argc, char **argv)
{
// Create sockaddr_in with server information
if (argc < 2) {
perror("No arguments passed\n");
return -1;
}
// make sure port number is above 1024 and set the port # to it
if (atoi(argv[1]) < 1024) {
return 1;
}
char *port = argv[1];
// parse the command line args for options -l and -N. -l specifies it will use a log and the following parameter is the filename. -N specifies the number of threads it will use and the following parameter will be a number
int opt;
uint8_t threadNum = 1;
char *logName = NULL;
while ((opt = getopt(argc - 1, argv + 1, "N:l:")) != -1) {
if (opt == 'N') {
threadNum = atoi(optarg);
}
else if (opt == 'l') {
logName = optarg;
}
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(port));
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t addrlen = sizeof(server_addr);
// Create server socket
int server_sockd = socket(AF_INET, SOCK_STREAM, 0);
// Need to check if server_sockd < 0, meaning an error
if (server_sockd < 0) {
perror("socket");
return 1;
}
// Configure server socket
int enable = 1;
// This allows you to avoid: 'Bind: Address Already in Use' error
int ret = setsockopt(server_sockd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
if (ret < 0) {
return EXIT_FAILURE;
}
// Bind server address to socket that is open
ret = bind(server_sockd, (struct sockaddr *) &server_addr, addrlen);
if (ret < 0) {
return EXIT_FAILURE;
}
// Listen for incoming connections
ret = listen(server_sockd, 5); // 5 should be enough, if not use SOMAXCONN
if (ret < 0) {
return EXIT_FAILURE;
}
struct threadObject thread[threadNum];
// Connecting with a client
struct sockaddr client_addr;
socklen_t client_addrlen = sizeof(client_addr);
// create a pthread array of size (number of threads). specify this will be using the handle connections function. join the threads together
#if 0
pthread_t thread_id[threadNum];
#endif
#if 1
struct threadObject *tsk = NULL;
int tskidx;
// clear out the thread structs
for (tskidx = 0; tskidx < threadNum; tskidx++) {
tsk = &thread[tskidx];
memset(tsk,0,sizeof(struct threadObject));
}
while (true) {
// accept connection
int client_sockd = accept(server_sockd, &client_addr, &client_addrlen);
pthread_mutex_lock(&global_mutex);
// join any previously completed threads
for (tskidx = 0; tskidx < threadNum; tskidx++) {
tsk = &thread[tskidx];
if (tsk->tsk_done) {
pthread_join(tsk->tsk_threadid,NULL);
tsk->tsk_inuse = 0;
tsk->tsk_done = 0;
}
}
// find unused task slot
for (tskidx = 0; tskidx < threadNum; tskidx++) {
tsk = &thread[tskidx];
if (! tsk->tsk_inuse)
break;
}
memset(tsk,0,sizeof(struct threadObject));
tsk->client_sockd = client_sockd;
tsk->tsk_inuse = 1;
pthread_mutex_unlock(&global_mutex);
// fire in the hole ...
pthread_create(&tsk->tsk_threadid, NULL, handle_connections, tsk);
}
#endif
#if 0
for (int i = 0; i < threadNum; i++) {
printf("\n[+] server is waiting...\n");
thread[i].client_sockd = accept(server_sockd, &client_addr, &client_addrlen);
handle_connections(&thread[i]);
// pthread_create(&thread_id[i], NULL, handle_connections, &thread[i]);
printf("Response Sent\n");
// close the current client socket
close(thread[i].client_sockd);
}
}
#endif
return EXIT_SUCCESS;
}

Multi-threaded Queue in Linux C

I'm writing a simple program that reads urls from a text file, and checks the validity of them using multi threaded programming. I used mutexes and condition variables to synchronize my threads but my app still crashes and after some debugging sessions I decided to get some help :)
The input of the file is a text file containing urls like:
http://www.youtube.com/
http://www.facebook.com/
And the output should be the aggregated result of the curl request on each url (whether it returns OK, UNKOWN or ERROR)
This is my code:
/*
* ex3.c
*/
#define _GNU_SOURCE
#include <curl/curl.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define REQUEST_TIMEOUT_SECONDS 2L
#define URL_OK 0
#define URL_ERROR 1
#define URL_UNKNOWN 2
#define QUEUE_SIZE 32
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
typedef struct {
int ok, error, unknown;
} UrlStatus;
typedef struct {
void **array;
int size;
int capacity;
int head;
int tail;
pthread_mutex_t mutex;
pthread_cond_t cv_empty; /* get notified when the queue is not full */
pthread_cond_t cv_full; /* get notified when the queue is not empty */
} Queue;
void queue_init(Queue *queue, int capacity) {
/*
* Initializes the queue with the specified capacity.
* This function should allocate the internal array, initialize its properties
* and also initialize its mutex and condition variables.
*/
queue->array = (void**)malloc(sizeof(void*) * capacity);
if (queue->array == NULL) {
perror("unable to allocate memory");
exit(EXIT_FAILURE);
}
queue->capacity = capacity;
queue->size = 0;
queue->head = 0;
queue->tail = 0;
pthread_mutex_init(&(queue->mutex), NULL);
pthread_cond_init(&(queue->cv_empty), NULL);
pthread_cond_init(&(queue->cv_full), NULL);
}
void enqueue(Queue *queue, void *data) {
/*
* Enqueue an object to the queue.
*
* TODO:
* 1. This function should be synchronized on the queue's mutex
* 2. If the queue is full, it should wait until it is not full
* (i.e. cv_empty)
* 3. Add an element to the tail of the queue, and update the tail & size
* parameters
* 4. Signal that the queue is not empty (i.e. cv_full)
*/
pthread_mutex_lock(&(queue->mutex));
while (queue->size >= QUEUE_SIZE) {
pthread_cond_wait(&(queue->cv_empty), &(queue->mutex));
}
if(queue->size == 0) {
queue->head = 0;
}
queue->array[queue->tail] = data;
queue->size++;
queue->tail++;
pthread_cond_signal(&(queue->cv_full));
pthread_mutex_unlock(&(queue->mutex));
}
void *dequeue(Queue *queue) {
/*
* Dequeue an object from the queue.
*
* TODO:
* 1. This function should be synchronized on the queue's mutex
* 2. If the queue is empty, it should wait until it is not empty (i.e. cv_full)
* 3. Read the head element, and update the head & size parameters
* 4. Signal that the queue is not full (i.e. cv_empty)
* 5. Return the dequeued item
*/
void *data;
pthread_mutex_lock(&(queue->mutex));
while (queue->size <= 0) {
pthread_cond_wait(&(queue->cv_full), &(queue->mutex));
}
queue->head++;
data = queue->array[queue->head];
queue->size--;
pthread_cond_signal(&(queue->cv_empty));
pthread_mutex_unlock(&(queue->mutex));
return data;
}
void queue_destroy(Queue *queue) {
/*
* Free the queue memory and destroy the mutex and the condition variables.
*/
int ret;
free(queue->array);
ret = pthread_mutex_destroy(&(queue->mutex));
if (ret != 0) {
handle_error_en(ret, "unable to destroy mutex");
}
ret = pthread_cond_destroy(&(queue->cv_empty));
if (ret != 0) {
handle_error_en(ret, "unable to destroy cv_empty condition variable");
}
ret = pthread_cond_destroy(&(queue->cv_full));
if (ret != 0) {
handle_error_en(ret, "unable to destroy cv_full condition variable");
}
}
void usage() {
fprintf(stderr, "usage:\n\t./ex3 FILENAME NUMBER_OF_THREADS\n");
exit(EXIT_FAILURE);
}
int count = 0;
int check_url(const char *url) {
CURL *curl;
CURLcode res;
long response_code = 0L;
int http_status = URL_UNKNOWN;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, REQUEST_TIMEOUT_SECONDS);
curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); /* do a HEAD request */
res = curl_easy_perform(curl);
if(res == CURLE_OK) {
res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
if (res == CURLE_OK &&
response_code >= 200 &&
response_code < 400) {
http_status = URL_OK;
} else {
http_status = URL_ERROR;
}
}
curl_easy_cleanup(curl);
}
return http_status;
}
typedef struct {
Queue *url_queue;
Queue *result_queue;
} WorkerArguments;
void *worker(void *args) {
/*
* TODO:
* 1. Initialize a UrlStatus (allocate memory using malloc, and zero it
* using memset)
* 2. Dequeue URL from url_queue, run check_url on it, and update results
* (don't forget to free() the url)
* 3. After dequeuing a NULL value:
* Enqueue results to result_queue and return
*/
WorkerArguments *worker_args = (WorkerArguments *)args;
UrlStatus *results = NULL;
char *url;
results = malloc(sizeof(UrlStatus));
if(results == NULL) {
perror("Could not allocate memory");
exit(-1);
}
memset(results, 0, sizeof(UrlStatus));
while((url = (char *)dequeue(worker_args->url_queue)) != NULL) {
switch(check_url(url)) {
case URL_OK:
results->ok++;
break;
case URL_UNKNOWN:
results->unknown++;
break;
case URL_ERROR:
results->error++;
break;
}
/*free(url);*/
}
enqueue(worker_args->result_queue, results);
return NULL;
}
typedef struct {
const char *filename;
Queue *url_queue;
} FileReaderArguments;
void *file_reader(void *args) {
/*
* TODO:
* 1. Open filename (use fopen, check for errors)
* 2. Use getline() to read lines (i.e. URLs) from the file (use errno to check for errors)
* 3. Copy each url to the heap (use malloc and strncpy)
* 4. Enqueue the URL to url_queue
* 5. Don't forget to free the line variable, and close the file (and check for errors!)
*/
FileReaderArguments *file_reader_args = (FileReaderArguments *)args;
FILE *toplist_file;
char *line = NULL;
char *url = NULL;
size_t len = 0;
ssize_t read = 0;
toplist_file = fopen(file_reader_args->filename, "r");
if (toplist_file == NULL) {
exit(EXIT_FAILURE);
}
while ((read = getline(&line, &len, toplist_file)) != -1) {
if (read == -1) {
perror("error reading file");
}
if(read == 1) continue; /*empty line*/
url = malloc(read);
if(url == NULL) {
perror("Could not allocate memory");
}
line[read-1] = '\0'; /* null-terminate the URL */
strncpy(url, line, read);
enqueue(file_reader_args->url_queue, url);
}
fclose(toplist_file);
return NULL;
}
typedef struct {
int number_of_threads;
Queue *url_queue;
Queue *result_queue;
} CollectorArguments;
void *collector(void *args) {
/*
* TODO:
* 1. Enqueue number_of_threads NULLs to the url_queue
* 2. Dequeue and aggregate number_of_threads thread_results
* from result_queue into results (don't forget to free() thread_results)
* 3. Print aggregated results to the screen
*/
CollectorArguments *collector_args = (CollectorArguments *)args;
UrlStatus results = {0};
UrlStatus *thread_results;
int i;
for(i= 0; i < collector_args->number_of_threads; i++) {
}
for(i= 0; i < collector_args->number_of_threads; i++) {
thread_results = dequeue(collector_args->result_queue);
results.ok += thread_results->ok;
results.error += thread_results->error;
results.unknown += thread_results->unknown;
/* free(thread_results);*/
}
printf("%d OK, %d Error, %d Unknown\n",
results.ok,
results.error,
results.unknown);
return NULL;
}
void parallel_checker(const char *filename, int number_of_threads) {
/*
* TODO:
* 1. Initialize a Queue for URLs, a Queue for results (use QUEUE_SIZE)
* 2. Start number_of_threads threads running worker()
* 3. Start a thread running file_reader(), and join it
* 4. Start a thread running collector(), and join it
* 5. Join all worker threads
* 6. Destroy both queues
*/
Queue url_queue, result_queue;
WorkerArguments worker_arguments = {0};
FileReaderArguments file_reader_arguments = {0};
CollectorArguments collector_arguments = {0};
pthread_t *worker_threads;
pthread_t file_reader_thread, collector_thread;
int i;
int err;
queue_init(&url_queue, QUEUE_SIZE);
queue_init(&result_queue, QUEUE_SIZE);
worker_arguments.url_queue = &url_queue;
worker_arguments.result_queue = &result_queue;
file_reader_arguments.filename = filename;
file_reader_arguments.url_queue = &url_queue;
collector_arguments.url_queue = &url_queue;
collector_arguments.result_queue = &result_queue;
collector_arguments.number_of_threads = number_of_threads;
worker_threads = (pthread_t *) malloc(sizeof(pthread_t) * number_of_threads);
if (worker_threads == NULL) {
perror("unable to allocate memory");
return;
}
curl_global_init(CURL_GLOBAL_ALL); /* init libcurl before starting threads */
for(i=0; i<number_of_threads; i++) {
err = pthread_create(&(worker_threads[i]), NULL, &worker, &worker_arguments);
if (err != 0) {
fprintf(stderr, "can't create thread :[%s]\n", strerror(err));
}
}
err = pthread_create(&file_reader_thread, NULL, &file_reader, &file_reader_arguments);
if (err != 0) {
fprintf(stderr, "can't create thread :[%s]\n", strerror(err));
}
err = pthread_join(file_reader_thread, NULL);
if (err != 0) {
fprintf(stderr, "can't join thread :[%s]\n", strerror(err));
}
err = pthread_create(&collector_thread, NULL, &collector, &collector_arguments);
if (err != 0) {
fprintf(stderr, "can't create thread :[%s]\n", strerror(err));
}
err = pthread_join(collector_thread, NULL);
if (err != 0) {
fprintf(stderr, "can't join thread :[%s]\n", strerror(err));
}
for(i=0; i<number_of_threads; i++) {
err = pthread_join(worker_threads[i], NULL);
if (err != 0) {
fprintf(stderr, "can't join thread :[%s]\n", strerror(err));
}
}
queue_destroy(&url_queue);
queue_destroy(&result_queue);
free(worker_threads);
}
int main(int argc, char **argv) {
if (argc != 3) {
usage();
} else {
parallel_checker(argv[1], atoi(argv[2]));
}
return EXIT_SUCCESS;
}
I think that I'm missing something with the synchronization mechanisms, can anyone spot where I was wrong?
Thanks a lot!!!
One multi-threading logic flaw is definitely:
There is no distinction between "worker input queue temporarily empty" and "file reader thread finished" - Your workers simply exit when there is nothing to read (even temporarily) on their input queue. So, if the file_reader thread produces queue entries slower than the workers consume them for any reason, the consumers will starve and die, leaving the producer with no consumer and, finally, a hang.

pthread_mutex_lock 100% cpu?

code:
local void*
s_accept_connections(tmpsock)
void* tmpsock;
{
int32_t newfd;
int32_t tmp;
SOCKADDR_IN newsockaddr;
pthread_t id;
Connection* newconn;
const char *s;
char **splited;
int i;
StringVec *p;
StringVec* next;
Socket* sock;
tmp = sizeof(newsockaddr);
p = NULL;
next = NULL;
sock = (Socket *)tmpsock;
if (!sock)
return 0;
while (true){
newfd = accept(sock->fd,(SOCKADDR *)&newsockaddr,&tmp);
if (newfd <0){
if (check_error_async()){
pthread_mutex_lock(&g_socket_mutex);
#ifdef _WIN32
Sleep(1000);
#else
sleep(1);
#endif
pthread_mutex_unlock(&g_socket_mutex);
continue;
}
}else{
newconn = (Connection *)MyMalloc(sizeof(*newconn));
newconn->fd = newfd;
newconn->addr = newsockaddr;
s = (const char *)inet_ntoa(newsockaddr.sin_addr);
p = split_string(s,".");
if (p != NULL){
splited = (char **)MyMalloc(sizeof(*splited) + 12);
i = 0;
for (; p != NULL; p = next){
if (p && p->next){
next = p->next;
}else{ break; }
splited[i] = p->value;
i++;
}
newconn->ip = swap_uint32_t((uint32_t)(atoi(splited[0])) + (atoi(splited[1]) << 8) + (atoi(splited[2]) << 16) + (atoi(splited[3]) << 24));
MyFree((char *)splited);
}else{
newconn->ip = 0;
}
newconn->closed = false;
newconn->state = 0;
newconn->state |= S_NEED_LOGIN;
pthread_mutex_init(&g_ping_mutex,NULL);
pthread_cond_init(&g_ping_cond,NULL);
pthread_create(&id,NULL,s_ping_thread,(void *)newconn);
a_conn(&sock->conn,newconn);
#ifndef NDEBUG
_("Accepting connection...\n");
#endif
if (sock->has_callback){
sock->func(newconn);
#ifndef NDEBUG
_("Accepted connection\n");
#endif
}
}
}
return 0;
}
void
start_accept(sock,join)
Socket* sock;
bool join;
{
pthread_t id;
pthread_attr_t attr;
if (!sock)
return;
if (!sock->conn){
sock->conn = (Connection *)MyMalloc(sizeof(*sock->conn));
if (!sock->conn)
return;
}
set_nonblocking(sock->fd);
set_nonblocking(sock->conn->fd);
pthread_attr_init(&attr);
pthread_mutex_init(&g_socket_mutex,NULL);
if (join){
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
}else{
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
}
pthread_create(&id,&attr,s_accept_connections,sock);
if (join){
pthread_join(id,NULL);
pthread_attr_destroy(&attr);
pthread_mutex_destroy(&g_socket_mutex);
}
}
It simply gives 100% cpu, any ideas? if more code needed, ill post
What makes you believe that pthread_mutex_lock() is responsible for the CPU usage ?
Use a debugger to find out what is happening.
My guess is there is something wrong with your socket, making your accept() call non-blocking.
Check the return value/message (with perror() if you are running linux).
EDIT :
You need to know which piece of code is looping a debugger can help you to find this.
You have a while(true) loop that is very likely to be responsible for the enless loop and the 100% CPU usage. It should be ok since you have a call to accept() (here :newfd = accept(sock->fd,(SOCKADDR *)&newsockaddr,&tmp);) that is supposed to stop the thread/process until the next client connection. But if your socket is not correctly initialized accept() may return an error without waiting.

Resources