💻 Job Executor Server is a multithreaded server designed to handle and execute jobs submitted by multiple clients (jobCommanders). It uses a producer-consumer model with a queue system, allowing jobs to be executed by worker threads. The server and its components are highly configurable, supporting dynamic thread pools, job cancellation, and job monitoring. Clients communicate with the server via sockets, submitting commands for job execution and server management.
The project consists of the following 6 folders:
- bin: Contains the executables generated from the compilation.
- build: Contains the object files generated from the compilation.
- include: Contains all the
.hfiles. Theincludes.hfile includes all the other headers and is used by all.cfiles. - out: Contains temporary
pid.outputfiles generated during job execution, which are used for sending the job output to the correspondingjobCommander. These files will be deleted after the program's execution. - src: Contains the source code.
- tests: Includes tests provided in the first assignment, with some modifications (explained below).
Note: The
libfolder is omitted because the program does not use external libraries.
- queue.h: Implements the queue. This includes functions that use function pointers for comparing (
CompareFunc), writing to a file descriptor (WriteFunc), and freeing memory (FreePtr). The queue follows a producer-consumer model, using condition variables and mutexes to block threads when necessary. - structs.h: Contains the structures and related functions used in the project. Specifically, it defines the
Serverstructure and theIdentifiersstored in the queue. - sockets.h: Contains functions for creating, reading/writing to a socket, and deleting a socket.
- threads.h: Defines the server's thread functions, thread creation, joining of worker threads, and the main loop that accepts new connections from jobCommanders.
- utils.h: Contains various utility functions for error checking, pointer validation, argument checking, etc.
- parsing.h: Implements functions responsible for "understanding" input data and executing the corresponding operations.
-
run_server.sh: A helper script that:
- Compiles the program and grants execution permissions to existing tests (if not already set), as well as to the generated executables.
- Starts the server.
The program can be compiled either by using the
makecommand or by running this script. Execution syntax:./run_server.sh <port> <bufferSize> <threadPoolSize>Default values for these arguments (if not provided) are7856,8, and5for the port number, queue size, and number of worker threads, respectively. -
run_test.sh: A helper script that:
- Runs the test with the specified test number (from 1 to 8).
Execution syntax:
./run_test.sh <test_number>Each test uses default valueslinux18.di.uoa.grand port7856, unless provided with other arguments (the first argument is the machine number, the second is the port number).
- The server is created and the arguments are checked for validity.
- The queue and
main_threadare created. Themain_threadspawns the specified number ofworker_threads. - A socket is created and the server enters an infinite loop waiting for connections.
- When a
jobCommanderconnects with valid arguments, the server creates acontroller_thread, which handles the commands received through the socket. - The server sends a message to the
jobCommander, and the commander terminates unless it is waiting for a job output. - If a
jobCommandersends anexitcommand, the server completes the currently running jobs. - An informational message is sent to all
jobCommanderswaiting for job results, indicating that the server shut down before job execution. - The server terminates.
The only signal used is SIGUSR1, sent to the main_thread to indicate server termination after completing all running jobs. This signal is sent by a controller_thread when an exit command is issued.
-
issueJob: The
jobCommandersends a message, which is read by thecontroller_thread. If there is space in the queue, a structure is created and enqueued. If the queue is full, the thread blocks until a spot opens. Once enqueued, thejobCommanderis notified, and the job output is sent once available. Aworker_threadhandles the job execution, stores the output in a temporary file, and sends it to thejobCommanderin chunks of 1024 bytes. Afterward, the server deletes the temporary file. -
setConcurrency: Adjusts the degree of concurrency based on the input. If the number is negative, concurrency is set to 0. There is no upper limit, and the user can specify any number. If the concurrency increases, new threads start processing queued jobs. If concurrency decreases, currently running threads continue, but no new jobs are processed until the number of running threads matches the concurrency level.
-
stop: If the given job is in the queue, it is removed, and a success message is sent to the
jobCommander. If the job is already executing, no action is taken, and a message is sent indicating that the job was not found. -
poll: Sends the contents of the queue (i.e., the jobs waiting to be executed) to the
jobCommander. If the queue is empty, a "Queue is empty" message is displayed. -
exit: Sends a
SIGUSR1signal to themain_thread, stopping the server after completing all currently running jobs. The queue is cleared, and alljobCommanderswaiting for results are notified.NULLvalues are inserted into the queue for eachworker_threadto signal them to terminate.
In all commands except issueJob, the controller_thread handles execution, updating concurrency, removing jobs, and signaling the main_thread for termination. worker_threads are responsible for executing jobs in the queue.