/** * @file supervisor.c * @date 2018-05-21 * * @brief Server program which reads solutions and orders them. */ #include "common.h" /** * @details Global variable for the program name. */ static const char *pname; /** * @details Struct for shared memory. */ struct circ_buf *shared; /** * @details File descriptor for shared memory. */ static int shmfd; /** * @details Semaphore which counts the used space in the shared memory array. */ static sem_t *sUsedSpace; /** * @details Semaphore which counts the free space in the shared memory array. */ static sem_t *sFreeSpace; /** * @details Semaphore to ensure exclusive access to shared memory from generators. */ static sem_t *sWriteEnd; /** * @details Struct for signal handling. */ struct sigaction sa; /** * @details Array which remembers the best solution so far. */ static int bestSolution[MAX_ITEMS]; /** * @details Variable which gets set on signal received. */ static volatile sig_atomic_t quit = 0; /** * @details Signal handler. * @param signal The number of the received signal. * @return This function has no return value. */ static void sig_handler(int signum) { quit = 1; } /** * @details Prints the synopsis. * @param void This function takes no parameters. * @return Always non-zero. */ static void usage(void) { fprintf(stderr, "SYNOPSIS\n" "\tsupervisor\n"); exit(EXIT_FAILURE); } /** * @details Cleanup function which sets the quit variable in shared memory to 1 * and closes shared memory and semaphores. * @param void This function takes no parameters. * @return 0 on success, non-zero on failure. */ static void cleanup(void) { shared->quit = 1; sem_wait(sUsedSpace); sem_post(sWriteEnd); sem_post(sFreeSpace); if (munmap(shared, sizeof(*shared)) == -1) { perror("munmap"); exit(EXIT_FAILURE); } close(shmfd); if (shm_unlink(SHM_NAME) == -1) { perror("shm_unlink"); exit(EXIT_FAILURE); } sem_close(sUsedSpace); sem_close(sFreeSpace); sem_close(sWriteEnd); sem_unlink(SEM_USED_SPACE); sem_unlink(SEM_FREE_SPACE); sem_unlink(SEM_WRITE_END); exit(EXIT_SUCCESS); } /** * @details Function which initializes the shared memory buffer and sets the * initial bestSolution to INT_MAX. * @param void This function takes no parameters. * @return This function has no return value. */ static void initCircBuf(void) { shared->quit = 0; for (int i = 0; i < MAX_ITEMS; i++) { shared->data[i] = -1; } for (int i = 0; i < MAX_ITEMS; i++) { bestSolution[i] = INT_MAX; } } /** * @details Custom function for sem_wait which also listens for signals. * @param sem The semaphore to be decremented. * @return 0 on success, non-zero on failure. */ static void wait_sem(sem_t *sem) { while (sem_wait(sem) == -1) { // interrupted by syscall? if (errno == EINTR) { if (quit == 1) { cleanup(); exit(EXIT_SUCCESS); } else continue; } cleanup(); exit(EXIT_FAILURE); } return; } /** * @details Function which reads a solution from the circular buffer and compares * it to the current bestSolution. If smaller, replace bestSolution. * @param sUsedSpace Semaphore which counts the used space. * @param sFreeSpace Semaphore which counts the free space. * @return This function has no return value. */ static void getSolution(sem_t *sUsedSpace, sem_t *sFreeSpace) { int solution[MAX_ITEMS]; for (int i = 0; i < MAX_ITEMS; i++) { solution[i] = -1; } for (int i = 0; i < MAX_ITEMS; i++) { wait_sem(sUsedSpace); solution[i] = shared->data[shared->head]; shared->head = (shared->head + 1) % MAX_ITEMS; sem_post(sFreeSpace); } // Code for testing if new solution is better than old int bestSolutionLength = 0; int newSolutionLength = 0; // Compute length of current bestSolution if ((bestSolution[0] != -1) && (bestSolution[0] != INT_MAX)) { for (int i = 0; i < MAX_ITEMS; i++) { if (bestSolution[i] == -1) { bestSolutionLength = i; break; } } } else if (bestSolution[0] == INT_MAX) { bestSolutionLength = 17; } // Compute length of newSolution if (solution[0] != -1) { int i = 0; while ((solution[i] != -1) && (i != (MAX_ITEMS - 1))) { i++; } newSolutionLength = i; } if (newSolutionLength == 0) { printf("[%s] The graph is acyclic!\n", pname); quit = 1; return; } if (newSolutionLength < bestSolutionLength) { // Set all entries of bestSolution to -1 for (int i = 0; i < MAX_ITEMS; i++) { bestSolution[i] = -1; } // Copy all entries from solution to bestSolution for (int i = 0; i < MAX_ITEMS; i++) { bestSolution[i] = solution[i]; } printf("[%s] Solution with %d edges:", pname, newSolutionLength/2); for (int i = 0; i < newSolutionLength; i += 2) { printf(" %d-%d", bestSolution[i], bestSolution[i+1]); } printf("\n"); } } /** * The main entry point of the program. * @details Sets the signal handler, sets up shared memory and semaphores and * initializes the shared memory buffer. Loops until solution with 0 * edges found or signal received. * @param argc The number of supplied commandline arguments. * @param argv The array of supplied commandline arguments. * @return 0 on success, non-zero on failre. */ int main(int argc, char *argv[]) { if (argc != 1) usage(); pname = argv[0]; struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = &sig_handler; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); shmfd = shm_open(SHM_NAME, O_RDWR | O_CREAT, PERMISSION); if (shmfd == -1) { perror("[./supervisor] shm_open"); exit(EXIT_FAILURE); } if (ftruncate(shmfd, sizeof(*shared)) == -1) { perror("[./supervisor] ftruncate"); exit(EXIT_FAILURE); } shared = mmap(NULL, sizeof(*shared), PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0); if (shared == MAP_FAILED) { perror("[./supervisor] mmap"); exit(EXIT_FAILURE); } sFreeSpace = sem_open(SEM_FREE_SPACE, O_CREAT | O_EXCL, PERMISSION, MAX_ITEMS); sUsedSpace = sem_open(SEM_USED_SPACE, O_CREAT | O_EXCL, PERMISSION, 0); sWriteEnd = sem_open(SEM_WRITE_END, O_CREAT | O_EXCL, PERMISSION, 1); initCircBuf(); while (quit == 0) { getSolution(sUsedSpace, sFreeSpace); } cleanup(); return 0; }