diff --git a/src/channel/c_blc_channel.cpp b/src/channel/c_blc_channel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..436855714a0c198b0f0443094237081a399d3678 --- /dev/null +++ b/src/channel/c_blc_channel.cpp @@ -0,0 +1,294 @@ +/* + Basic Library for C/C++ (blclib) + Copyright ETIS — ENSEA, Université de Cergy-Pontoise, CNRS (2011 - 2015) + + Author: Arnaud Blanchard + + This software is governed by the CeCILL v2.1 license under French law and abiding by the rules of distribution of free software. + You can use, modify and/ or redistribute the software under the terms of the CeCILL v2.1 license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info". + As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license, + users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors have only limited liability. + In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software, + that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge. + Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured + and, more generally, to use and operate it in the same conditions as regards security. + The fact that you are presently reading this means that you have had knowledge of the CeCILL v2.1 license and that you accept its terms. * + + + Created on: Oct 9, 2014 + Author: Arnaud Blanchard + */ + +#include "blc_channel.h" + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <pthread.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/mman.h> //mmap +#include <sys/stat.h> // mode S_ ... constants +#include <sys/time.h> //gettimeofday +#include <semaphore.h> +#include <stack> +#include <vector> +#include <tuple> + +#include "blc_realtime.h" +#include "blc_text.h" + +#define TMP_BUFFER_SIZE 4096 +#define BLC_CHANNELS_LIST_PATH "/tmp/blc_channels.txt" + +using namespace std; + +static int blc_channel_id_max = 0; +static sem_t *blc_channel_event[2]={NULL, NULL}; +static int blc_channel_event_id=0; +static void (*blc_channel_event_callback)(void*)=NULL; +static void *blc_channel_event_user_data=NULL; + +pthread_t pthread; + +START_EXTERN_C + +// This is especially for binding to external langage (e.g. python) +blc_channel *blc_channel_new(char const *name, int mode, uint32_t type, uint32_t format, int dims_nb, int length0, ...){ + va_list arguments; + vector<size_t> lengths(dims_nb, length0); + + va_start(arguments, length0); + for (int i=1; i<dims_nb; i++) lengths[i] = va_arg(arguments, size_t); + va_end(arguments); + + return new blc_channel(name, mode, type, format, lengths); +} + +blc_channel *blc_channel_new_open(char const *name, int mode){ + return new blc_channel(name, mode); +} + + + +// This is especially for binding to external langage (e.g. python) +// Delete the channel. This is different from destroy which deinits the channel but does not remove the memory. +void blc_channel_delete(blc_channel *channel){ + delete channel; +} + +void blc_remove_channel_with_name(char const *name){ + + FILE *list_file; + blc_channel info; + fpos_t write_position, read_position; + char *buffer[TMP_BUFFER_SIZE]; + size_t data_read; + off_t offset; + // char control_filename[NAME_MAX]; + char tmp_name[NAME_MAX]; + + SYSTEM_ERROR_CHECK(list_file=fopen(BLC_CHANNELS_LIST_PATH, "r+"), NULL, "impossible to open " BLC_CHANNELS_LIST_PATH); + + while(!feof(list_file)) + { + fgetpos(list_file, &write_position); + info.fscan_info(list_file, 1); + if (strcmp(info.name, name) == 0) + { + fgetpos(list_file, &read_position); //we remove the line by copying the end of the file here. + do + { + fsetpos(list_file, &read_position); + data_read = fread(buffer, 1, TMP_BUFFER_SIZE, list_file); + fgetpos(list_file, &read_position); + if (data_read==0) if (!feof(list_file)) EXIT_ON_ERROR("Reading file" BLC_CHANNELS_LIST_PATH); + fsetpos(list_file, &write_position); + SYSTEM_SUCCESS_CHECK(fwrite(buffer, 1, data_read, list_file), data_read, ""); + fgetpos(list_file, &write_position); + }while(data_read); + + SYSTEM_ERROR_CHECK(offset = ftell(list_file), -1, ""); + SYSTEM_ERROR_CHECK(ftruncate(fileno(list_file), offset), -1, ""); + SYSTEM_ERROR_CHECK(fclose(list_file), -1, ""); + break; + } + } + if (info.id==blc_channel_id_max) blc_channel_id_max--; + + SYSTEM_ERROR_CHECK(shm_unlink(name), -1, "unlinking blc_channel '%s'", name); + sprintf(tmp_name, "blc_channel%d_sem_new_data0", info.id); + SYSTEM_ERROR_CHECK(sem_unlink(tmp_name), -1, "Unlinking sem_new_data '%s' for blc_channel '%s'", tmp_name, name); + sprintf(tmp_name, "blc_channel%d_sem_ack_data0", info.id); + SYSTEM_ERROR_CHECK(sem_unlink(tmp_name), -1, "Unlinking sem_ack_data '%s' for blc_channel '%s'", tmp_name, name); + + blc_channel_post_event(); + +} + +// Envoie un message d'erreur avec name_of_file, name_of_function, number_of_line et affiche le message formate avec les parametres variables. Puis exit le programme avec le parametre EXIT_FAILURE. To be used with EXIT_ON_ERROR. +void blc_channel_fatal_error(blc_channel *channel, const char *name_of_file, const char* name_of_function, int numero_of_line, const char *message, ...){ + va_list arguments; + va_start(arguments, message); + color_eprintf(BLC_BRIGHT_RED, "\n%s: %s \t %s \t %i :\nError: ", blc_program_name, name_of_file, name_of_function, numero_of_line); + color_veprintf(BLC_BRIGHT_RED, message, arguments); + va_end(arguments); + channel->fprint_debug(stderr); + fprintf(stderr, "\n\n"); + fflush(stderr); + raise(SIGABRT); + exit(EXIT_FAILURE); +} + +void blc_channel_init(blc_channel *channel, const char *name, int mode, uint32_t type, uint32_t format, int dims_nb, int length0, ...) +{ + va_list arguments; + vector<size_t> lengths(dims_nb, length0); + + va_start(arguments, length0); + for(int i=1; i<dims_nb; i++) lengths.push_back(va_arg(arguments, size_t)); + va_end(arguments); + new (channel)blc_channel(name, mode, type, format, lengths); +} + +void blc_channel_open(blc_channel *channel, const char *name, int mode){ + channel->open(name, mode); +} + +void blc_channel_create_or_open(blc_channel *channel, const char *name, int mode, uint32_t type, uint32_t format, int dims_nb, int length0, ...){ + int created; + va_list arguments; + vector<size_t> lengths(dims_nb, length0); + va_start(arguments, length0); + for (int i=1; i<dims_nb; i++) lengths.push_back(va_arg(arguments, size_t)); + va_end(arguments); + new (channel) blc_channel(name, mode, type, format, lengths); +} + +//Search for channel name whatever is start by /,: ,^ or . +int blc_channel_get_info_with_name(blc_channel *info, char const *name){ + blc_channel tmp_info; + FILE *file; + int id=-1; + + file = fopen(BLC_CHANNELS_LIST_PATH, "r"); + if (file == NULL){ + if (errno==ENOENT) return -1; + else EXIT_ON_SYSTEM_ERROR("opening '" BLC_CHANNELS_LIST_PATH"'."); + } + + while(fscanf(file, "%*[ \t\n]")!=EOF){ + tmp_info.fscan_info(file, 1); + if ((tmp_info.id != -1) && (strcmp(tmp_info.name+1, name+1)==0)){ //We do not test first char '/' or '.' + id=tmp_info.id; + *info=tmp_info; + break; + } + } + fclose(file); + return id; +} + +int blc_channel_get_all_infos(struct blc_channel **channels_infos){ + return blc_channel_get_all_infos_with_filter(channels_infos, nullptr); +} + +int blc_channel_get_all_infos_with_filter(blc_channel **channels_infos, char const *filter){ + + vector<blc_channel> channels = blc_channel::get_all_infos(filter); + *channels_infos=MANY_ALLOCATIONS(channels.size(), blc_channel); + memcpy(channels_infos, channels.data(), sizeof(blc_channel)*channels.size()); //This copy is to incompatibility between c and c++ + return channels.size(); +} + +void blc_channel_close_all(blc_channel *channels, int channels_nb) +{ + blc_channel *channel; + FOR_EACH_INV(channel, channels, channels_nb) channel->~blc_channel(); +} + +int blc_channel_remove(blc_channel * channel) +{ + if(channel == NULL) EXIT_ON_ERROR("The channel is NULL"); + else channel->remove(); + return 1; +} + +static void init_blc_channel_sem_event(){ + SYSTEM_ERROR_CHECK(blc_channel_event[0]=sem_open("blc_channel_event0", O_CREAT, S_IRWXU, 0), SEM_FAILED, NULL); + SYSTEM_ERROR_CHECK(blc_channel_event[1]=sem_open("blc_channel_event1", O_CREAT, S_IRWXU, 0), SEM_FAILED, NULL); +} + +void blc_channel_post_event(){ + if (blc_channel_event[0]==NULL || blc_channel_event[1]==NULL) init_blc_channel_sem_event(); + + //If the event is already free it is not the good one. We free the other one to trigger the event + if (sem_trywait(blc_channel_event[blc_channel_event_id])==0){ + SYSTEM_ERROR_CHECK(sem_post(blc_channel_event[blc_channel_event_id]), -1, NULL); + SYSTEM_ERROR_CHECK(sem_post(blc_channel_event[1-blc_channel_event_id]), -1, NULL); + } + else { //If the event is not free, we free it to trigger the event + if (errno!=EAGAIN) EXIT_ON_SYSTEM_ERROR(NULL); + SYSTEM_ERROR_CHECK(sem_post(blc_channel_event[blc_channel_event_id]), -1, NULL); + } + blc_channel_event_id=1-blc_channel_event_id; +} + + +/**Block until event. It may produce false positive in connection */ +static void *blc_channel_thread_manager(void*){ + int i=0; + + //If the event is already free it is not the good one. We will wait for the other one + if (sem_trywait(blc_channel_event[i])==0){ + SYSTEM_ERROR_CHECK(sem_post(blc_channel_event[i]), -1, NULL); + i=1-i; + } + + while(1){ + //We wait for event + SYSTEM_ERROR_CHECK(sem_wait(blc_channel_event[i]), -1, NULL); + //We free event for the others + SYSTEM_ERROR_CHECK(sem_post(blc_channel_event[i]), -1, NULL); + i=1-i; + while(sem_trywait(blc_channel_event[i])==0); + if (errno!=EAGAIN) EXIT_ON_SYSTEM_ERROR(NULL); + blc_channel_event_callback(blc_channel_event_user_data); + } +} + +void blc_channel_check_for_event(void (*callback)(void*), void *user_data) { + if (blc_channel_event[0]==NULL || blc_channel_event[1]==NULL) init_blc_channel_sem_event(); + + blc_channel_event_callback=callback; + blc_channel_event_user_data=user_data; + BLC_PTHREAD_CHECK(pthread_create(&pthread, NULL, blc_channel_thread_manager, NULL), NULL); +} + +void blc_channel_destroy(blc_channel *channel){ + delete channel; +} + +void blc_channel_wait_new_data(void *channel_pt){ + blc_channel *channel=(blc_channel*)channel_pt; + SYSTEM_ERROR_CHECK(sem_wait(channel->sem_new_data), -1, "Waiting new data for channel '%s'", channel->name); +} + +void blc_channel_wait_ack_data(void *channel_pt){ + blc_channel *channel=(blc_channel*)channel_pt; + SYSTEM_ERROR_CHECK(sem_wait(channel->sem_ack_data), -1, "Waiting ack data for channel '%s'", channel->name); +} + +void blc_channel_post_new_data(void *channel_pt){ + blc_channel *channel=(blc_channel*)channel_pt; + SYSTEM_ERROR_CHECK(sem_post(channel->sem_new_data), -1, "Posting new data for channel '%s'", channel->name); +} + +void blc_channel_post_ack_data(void *channel_pt){ + blc_channel *channel=(blc_channel*)channel_pt; + SYSTEM_ERROR_CHECK(sem_post(channel->sem_ack_data), -1, "Posting ack data for channel '%s'", channel->name); +} + +END_EXTERN_C +