Почему атомарная функция-член test_and_set возвращает false - PullRequest
0 голосов
/ 10 ноября 2019

У меня есть небольшая программа, которая прослушивает порт 80 с помощью сокетов и отправляет Hello World. Я запускаю свою программу на Ubuntu 18 и отправляю запросы из браузера с другого хоста.

Первые несколько запросов, которые моя программа принимает успешно и успешно отвечает.

Но в конце концов после любого запросаможет произойти ошибка, при которой строка sock = this-> sock_backlog.front (); устанавливает переменную sock 0. Строка 20, файл task.cpp.

И эта часть кода повторяется в цикле, даже если нет входящих соединений, пока не произойдет ошибка сегментации.

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

Как видите, в этой программе я запустил все потоки заранее. После запуска всех потоков, он просто добавляет подключенные сокеты в очередь. И любой свободный поток получает один из этих сокетов, используя атомарный.

Вывод из консоли

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

Что не так в этом коде? Может быть неправильно использовать atomic и функция test_and_set возвращает false только для одного потока?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...