dimanche 10 novembre 2019

Why atomic member function test_and_set returns false

I have little program which listen port 80 using sockets and send Hello World. I run my program on Ubuntu 18, and send requests from browser from another host.

First few requests my program accept successfully and successfully respond.

But in the end, after any request, an error may occur in which the line sock = this-> sock_backlog.front (); sets sock variable 0. Line 20 file task.cpp.

And this part of code repeats in a cycle even if no incomming connections until Segmentation fault happens.

main.cpp

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <thread>
#include "taks.h"

using namespace std;
using namespace web;

int main()
{
    task *tsk = new task;
    thread task_mgr = thread(&task::manager, tsk);
    int sock, newsock, sockaddrlen;
    struct sockaddr_in sockaddr;

    if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        cout << "Socket create error: " << errno << endl;

        return 0;
    }

    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.s_addr = INADDR_ANY;
    sockaddr.sin_port = htons(PORT);
    sockaddrlen = sizeof(sockaddr);

    if(bind(sock, (struct sockaddr *) &sockaddr, sockaddrlen) < 0) {
        cout << "Socket bind error: " << errno << endl;

        return 0;
    }

    if(listen(sock, 0) < 0) {
        cout << "Socket listen error: " << errno << endl;

        return 0;
    }

    cout << "Start listening" << endl;

    while(true) {
        newsock = accept(sock, (struct sockaddr *) &sockaddr, (socklen_t *) &sockaddrlen);

        if(newsock < 0) {
            cout << "Socket accept error: " << errno << endl;

            continue;
        }

        cout << "Socket accepted: " << newsock << endl;

        if(!tsk->push_backlog(newsock)) {
            cout << "Reject connection: " << newsock << endl;

            if(close(newsock) < 0) {
                cout << "Socket close error: " << errno << endl;
            }
        }
    }

    //task_mgr.join();

    return 0;
}

task.h

#include <thread>
#include <queue>
#include <atomic>
#include "defs.h"

using namespace std;

namespace web
{
    class task
    {
        private:
            thread tasks[MAXLCON];
            queue<int> sock_backlog;
            atomic_flag flag = ATOMIC_FLAG_INIT;

            void accept_task(int);

        public:
            void manager();
            bool push_backlog(int);
    };
}

task.cpp

#include <iostream>
#include "taks.h"
#include <unistd.h>
#include <sys/socket.h>

using namespace std;
using namespace web;

void task::accept_task(int task_id)
{
    int sock;

    while(true) {
        if(this->sock_backlog.empty()) {
            this_thread::yield();
            continue;
        }

        if(!this->flag.test_and_set()) {
            sock = this->sock_backlog.front();
            cout << "Thread #" << task_id << " got socket " << sock << " flag " << this->flag.test_and_set() << endl;

            if(send(sock, "Hello World", 11, 0) < 0) {
                cout << "Socket send error: " << errno << endl;
            } else {
                cout << "Sended to " << sock << endl;
            }

            if(close(sock) < 0) {
                cout << "Socket close error: " << errno << endl;
            }

            this->sock_backlog.pop();
            this->flag.clear();
        }
    }
}

void task::manager()
{
    int f;

    for(f = 0; f < MAXLCON; f++) {
        this->tasks[f] = thread(&task::accept_task, this, f);
    }
}

bool task::push_backlog(int sock)
{   
    if(this->sock_backlog.size() < MAXBLOG) {
        this->sock_backlog.push(sock);
        return true;
    } else {
        return false;
    }
}

defs.h

#ifndef MAXLCON
#define MAXLCON 10
#endif

#ifndef MAXBLOG
#define MAXBLOG 20
#endif

#ifndef PORT
#define PORT 80
#endif

As you can see, in this program i have run all threads in advance. After starting all threads, It just add connected sockets to the queue. And any free thread receives one this sockets using atomic.

Output from console

Start listening
Socket accepted: 5
Thread #2 got socket 5 flag 1
Sended to 5
Socket accepted: 4
Thread #6 got socket 4 flag 1
Sended to 4
Socket accepted: 5
Thread #7 got socket 5 flag 1
Sended to 5
Thread #5 got socket 0 flag 1
Socket send error: 88
Thread #5 got socket 0 flag 1
Socket send error: 9
Socket close error: 9
Thread #5 got socket 0 flag 1
Socket send error: 9
Socket close error: 9
Thread #5 got socket 0 flag 1
Socket send error: 9
Socket close error: 9
Thread #5 got socket 0 flag 1
Socket send error: 9
Socket close error: 9
Thread #5 got socket 0 flag 1
Socket send error: 9
Socket close error: 9
Thread #4 got socket 0 flag 1
Socket send error: 9
Socket close error: 9
Segmentation fault

What's wrong in this code? May be incorrect using of atomic and function test_and_set returns false to mere than one thread?

Aucun commentaire:

Enregistrer un commentaire