Skip to content

Instantly share code, notes, and snippets.

@Alexis-D
Created February 20, 2012 13:15
Show Gist options
  • Save Alexis-D/1869142 to your computer and use it in GitHub Desktop.
Save Alexis-D/1869142 to your computer and use it in GitHub Desktop.
Printer Lab
all:
g++ -o printer -std=c++0x -Wall -pedantic -lpthread printer.cpp
#include <iomanip>
#include <iostream>
#include <pthread.h>
#include <queue>
#include <semaphore.h>
#include <unistd.h>
#include <vector>
#define NPRINTERS 5
typedef size_t Printer; // a printer just store the number of jobs done
typedef size_t Job; // a job is identified by its id (but we don't use it!)
std::priority_queue<Printer, std::vector<Printer>, std::greater<Printer>>
printers_queue; // use to prioritize the less-used printers
sem_t jobs_sem; // used to keep count of jobs
pthread_mutex_t printers_mutex, jobs_mutex; // lock printers_queue &
// printers_vect, and jobs_queue
std::queue<Job> jobs_queue; // hold the queue of jobs
std::vector<Printer> printers_vect; // "constant" ref to vectors
// we could do without it if "really"
// needed...
// display the load to the user
void* show_progress(void*) {
// always show two decimal places
std::cout << std::fixed << std::setprecision(2);
while(true) {
// clear screen && go home (top left)
std::cout << "\x1B[2J\x1B[H";
// total number of jobs done
int total = 0;
pthread_mutex_lock(&printers_mutex);
for(auto p : printers_vect) {
total += p;
}
pthread_mutex_unlock(&printers_mutex);
// because / 0 otherwise
if(total) {
size_t idx = 0;
for(auto p : printers_vect) {
// printer "id"
std::cout << "#" << idx << " ";
// progress bar
double ratio = (double)p / total;
size_t width = 50, load = ratio * width,
free = width - load;
std::cout << "\t[";
for(size_t i = 0; i < load; ++i) {
std::cout << "#";
}
for(size_t i = 0; i < free; ++i) {
std::cout << " ";
}
std::cout << "]";
// percentage
std::cout << " " << (ratio * 100.);
std::cout << "%\t(" << p << ")\n";
++idx;
}
std::cout << total << " jobs done so far (avg: ";
std::cout << ((double)total / NPRINTERS);
std::cout << " jobs/printer)." << std::endl;
usleep(500000);
}
}
return NULL;
}
// add job, sleep, loop
void* create_job(void*) {
size_t id = 0;
while(true) {
pthread_mutex_lock(&jobs_mutex);
jobs_queue.push(id);
pthread_mutex_unlock(&jobs_mutex);
sem_post(&jobs_sem);
usleep(rand() % 100000);
}
return NULL;
}
void* printer_handler(void *arg) {
Printer &p = printers_vect[(size_t)arg];
while(true) {
// wait new job
sem_wait(&jobs_sem);
pthread_mutex_lock(&printers_mutex);
// if it's my turn
if(printers_queue.top() == p) {
printers_queue.pop(); // we're busy
pthread_mutex_unlock(&printers_mutex);
pthread_mutex_lock(&jobs_mutex);
jobs_queue.front(); // get the job
jobs_queue.pop(); // remove it
pthread_mutex_unlock(&jobs_mutex);
usleep(rand() % 100000); // print
++p; // increase the number of finished jobs
pthread_mutex_lock(&printers_mutex);
printers_queue.push(p); // we're available
pthread_mutex_unlock(&printers_mutex);
}
else {
// resignal the job 'til someone get it
// non-deterministic
pthread_mutex_unlock(&printers_mutex);
sem_post(&jobs_sem);
}
}
return NULL;
}
int main(int argc, char *argv[]) {
sem_init(&jobs_sem, 0, 0);
printers_mutex = PTHREAD_MUTEX_INITIALIZER;
jobs_mutex = PTHREAD_MUTEX_INITIALIZER;
for(size_t i = 0; i < NPRINTERS; ++i) {
Printer p = Printer(0);
printers_queue.push(p);
printers_vect.push_back(p);
}
pthread_t show, create, printers[NPRINTERS];
pthread_create(&show, NULL, show_progress, NULL);
pthread_create(&create, NULL, create_job, NULL);
for(size_t i = 0; i < NPRINTERS; ++i) {
pthread_create(&printers[i], NULL, printer_handler, (void*)i);
}
pthread_join(show, NULL);
pthread_join(create, NULL);
for(size_t i = 0; i < NPRINTERS; ++i) {
pthread_join(printers[i], NULL);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment