ssize_t handler_get(gfcontext_t *ctx, char *path, void* arg){
	int fildes;
	ssize_t file_len, bytes_transferred;
	ssize_t read_len, write_len;
	char buffer[BUFFER_SIZE];

	if( 0 > (fildes = content_get(path)))
		return gfs_sendheader(ctx, GF_FILE_NOT_FOUND, 0);

	/* Calculating the file size */
	file_len = lseek(fildes, 0, SEEK_END);

	gfs_sendheader(ctx, GF_OK, file_len);

	/* Sending the file contents chunk by chunk. */
	bytes_transferred = 0;
	while(bytes_transferred < file_len){
		read_len = pread(fildes, buffer, BUFFER_SIZE, bytes_transferred);
		if (read_len <= 0){
			fprintf(stderr, "handle_with_file read error, %zd, %zu, %zu", read_len, bytes_transferred, file_len );
			gfs_abort(ctx);
			return -1;
		}
		write_len = gfs_send(ctx, buffer, read_len);
		if (write_len != read_len){
			fprintf(stderr, "handle_with_file write error");
			gfs_abort(ctx);
			return -1;
		}
		bytes_transferred += write_len;
	}

	return bytes_transferred;
}
ssize_t handle_with_file(gfcontext_t *ctx, char *path, void* arg){
	int fildes;
	size_t file_len, bytes_transferred;
	ssize_t read_len, write_len;
	char buffer[4096];
	char *data_dir = arg;

	strcpy(buffer,data_dir);
	strcat(buffer,path);

	if( 0 > (fildes = open(buffer, O_RDONLY))){
		if (errno == ENOENT)
			/* If the file just wasn't found, then send FILE_NOT_FOUND code*/ 
			return gfs_sendheader(ctx, GF_FILE_NOT_FOUND, 0);
		else
			/* Otherwise, it must have been a server error. gfserver library will handle*/ 
			return EXIT_FAILURE;
	}

	/* Calculating the file size */
	file_len = lseek(fildes, 0, SEEK_END);
	lseek(fildes, 0, SEEK_SET);

	gfs_sendheader(ctx, GF_OK, file_len);

	/* Sending the file contents chunk by chunk. */
	bytes_transferred = 0;
	while(bytes_transferred < file_len){
		read_len = read(fildes, buffer, 4096);
		if (read_len <= 0){
			fprintf(stderr, "handle_with_file read error, %zd, %zu, %zu", read_len, bytes_transferred, file_len );
			return EXIT_FAILURE;
		}
		write_len = gfs_send(ctx, buffer, read_len);
		if (write_len != read_len){
			fprintf(stderr, "handle_with_file write error");
			return EXIT_FAILURE;
		}
		bytes_transferred += write_len;
	}

	return bytes_transferred;
}
ssize_t handle_with_cache(gfcontext_t *ctx, char *path, void* arg)
{
	mqd_t msg_q;
	void *mem;
	struct request_info *req;
	struct shm_info *shm_blk;
	sem_t *sem1;
	sem_t *sem2;
	int file_in_cache;
	size_t file_size = 0;
	size_t bytes_transferred = 0;
	ssize_t write_len;
	size_t cache_file_size;

	pthread_mutex_lock(seg_q_mutex);
	while (steque_isempty(seg_q)) {
		pthread_cond_wait(seg_q_cond, seg_q_mutex);
	}
	shm_blk = (struct shm_info *)steque_pop(seg_q);
	pthread_mutex_unlock(seg_q_mutex);

	if ((sem1 = sem_open(shm_blk->sem1_name, O_CREAT, 0644, 0)) ==
	    SEM_FAILED) {
		perror("sem_open");
		return -1;
    	}
    	if ((sem2 = sem_open(shm_blk->sem2_name, O_CREAT, 0644, 0)) ==
	    SEM_FAILED) {
		perror("sem_open");
		return -1;
    	}
retry:
	errno = 0;
	msg_q = mq_open(QUEUE_NAME, O_WRONLY);
	if (msg_q == -1) {
		if (errno == ENOENT || errno == EACCES) {
			/* simplecached isn't ready yet, sleep and then retry */
			fprintf(stdout, "waiting for simplecached\n");
			sleep(2);
			goto retry;
		}
		perror("mq_open");
		return -1;
	}

	mem = mmap(NULL, seg_size, PROT_READ | PROT_WRITE, MAP_SHARED,
	    shm_blk->memfd, 0);
	if (!mem) {
		perror("mmap");
		return -1;
	}
	req = malloc(sizeof(*req) + strlen(path) + 1);
	memcpy(&req->mem_i, (char *)shm_blk + sizeof(int), sizeof(req->mem_i));
	req->mem_size = seg_size;
	req->file_len = strlen(path) + 1;
	strncpy(req->file_path, path, strlen(path));
	req->file_path[strlen(path)] = '\0';
	mq_send(msg_q, (char *)req, sizeof(*req) + strlen(path) + 1, 0);
	free(req);

	sem_wait(sem1);
	file_in_cache = *(int *)mem;
	sem_post(sem1);

	if (file_in_cache == -1) {
		 gfs_sendheader(ctx, GF_FILE_NOT_FOUND, 0);
		 goto finish;
	}
	sem_wait(sem1);
	file_size = *(size_t *)mem;
	cache_file_size = file_size;
	gfs_sendheader(ctx, GF_OK, file_size);
	sem_post(sem1);
	if (!file_size) {
		goto finish;
	}
	while (file_size) {
		sem_wait(sem1);
		bytes_transferred =  seg_size < file_size ?
		    seg_size : file_size;
		write_len = gfs_send(ctx, (char *)mem, bytes_transferred);
		if (write_len != bytes_transferred) {
			fprintf(stderr, "write error");
		}
		file_size -= bytes_transferred;
		sem_post(sem1);
	}
	sem_wait(sem1);
	file_size = *(size_t *)mem;
	if (file_size) {
		fprintf(stderr, "transfer error");
	}
	sem_post(sem1);

finish:
	mq_close(msg_q);
	sem_close(sem1);
	sem_close(sem2);
	sem_unlink(shm_blk->sem1_name);
	sem_unlink(shm_blk->sem2_name);
	munmap(mem, seg_size);
	pthread_mutex_lock(seg_q_mutex);
	steque_push(seg_q, shm_blk);
	pthread_mutex_unlock(seg_q_mutex);
	pthread_cond_signal(seg_q_cond);

	return cache_file_size;
}
ssize_t handle_with_cache(gfcontext_t *ctx, char *path, void *arg) {

    /* Get thread id */
    Workload *wld = (Workload *)arg;

    pthread_t tid = pthread_self();

    printf("entered %ld\n", tid);
    /* Sequence of steps to pop a segment number from the queue  */
    /* Read phase */
    /* Lock the mutex */
    pthread_mutex_lock(wld->sq_mtx_p);

    /* Wait until the queue is empty and writers are waiting */
    while(steque_isempty(wld->segqueue)) {
        fprintf(stderr, "[handle_with_cache, thread_id = %ld] Waiting for shm segment\n", tid);
        pthread_cond_wait(wld->sq_notempty_p, wld->sq_mtx_p);
    }

    /* Pop a segment number */
    long shm_segnum = (long) steque_pop(wld->segqueue);
    fprintf(stderr, "[handle_with_cache, thread_id = %ld] Popped shm segment %ld\n", tid, shm_segnum);

    /* unlock the mutex */
    fprintf(stderr, "[handle_with_cache, thread_id = %ld] Unlocking the mutex\n", tid);
    pthread_mutex_unlock(wld->sq_mtx_p);

    /* convert the shm_segnum to shm_segid string */
    fprintf(stderr, "[handle_with_cache, thread_id = %ld] converting segnum to segid\n", tid);
    char shm_segid[SEGID_LEN];
    memset(shm_segid, 0, SEGID_LEN);
    if (sprintf(shm_segid, "%ld", shm_segnum) < 0) {
        err_exit("handle_with_cache", "sprintf", 1);
    }

    /* Open descriptor for shared memory region */
    int shm_fd = shm_open(shm_segid, O_RDWR, 0);
    if (shm_fd == -1) {
        strerr_exit("handle_with_cache, shm_open", errno);
    }

    /* obtain the pointer to shared memory */
    Shm_Block *shmb_p;
    shmb_p = mmap(NULL, wld->shm_blocksize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (shmb_p == MAP_FAILED) {
        err_exit("handle_with_cache", "mmap failed", errno);
    }

    /* close shm file descriptor; its no longer needed */
    close(shm_fd);

    /* SHM data buffer starts after the shm metadata */
    char *shm_buf = (char *) shmb_p + sizeof(Shm_Block);

    /* Initialize writer (simplecached) semaphore to 1; initially available */
    if (sem_init(&shmb_p->sem_writer, 1, 1) == -1) {
        err_exit("handle_with_cache", "Unable to initialize writer semaphore.", errno);
    }

    /* Initialize reader (handle_with_cache) semaphore to 0; initially unavailable */
    if (sem_init(&shmb_p->sem_reader, 1, 0) == -1) {
        err_exit("handle_with_cache", "Unable to initialize reader semaphore.", errno);
    }

    /* Compose message to be sent to simplecached with URL, shm segment id and shm_blocksize */
    char msg[MQ_MSGSIZE];
    if(sprintf(msg, "%s %ld %zu", path, shm_segnum, wld->shm_blocksize) < 0 ) {
        err_exit("handle_with_cache", "sprintf", 1);
    }

    /* Send message */
    fprintf(stderr, "[handle_with_cache, thread_id = %ld] Sending message on mqueue\n", tid);
    if (mq_send(wld->mqd, msg, strlen(msg), 0) == -1)
        strerr_exit("handle_with_cache, mq_send", errno);

    /* Lock the reader */
    if (sem_wait(&shmb_p->sem_reader) == -1) {
        strerr_exit("handle_with_cache, sem_wait (hdr)", errno);
    }

    /* Read file size from simplecached */
    fprintf(stderr, "[handle_with_cache, thread_id = %ld] Reading file-size from simplecached\n", tid);
    int file_size = shmb_p->file_size;
    fprintf(stderr, "[handle_with_cache] Req path = %s, file_size = %d\n", path, file_size);
    /* send GFS header */

    if (file_size == -1) {
        fprintf(stderr, "[handle_with_cache, thread_id = %ld] FNF, about to lock mutex\n", tid);
        pthread_mutex_lock(wld->sq_mtx_p);
        fprintf(stderr, "[handle_with_cache, thread_id = %ld] Enqueuing the segnum %ld\n", tid, shm_segnum);
        steque_enqueue(wld->segqueue, (steque_item) shm_segnum);
        pthread_mutex_unlock(wld->sq_mtx_p);
        pthread_cond_broadcast(wld->sq_notempty_p);
        return gfs_sendheader(ctx, GF_FILE_NOT_FOUND, 0);
    }

    /* unlock the writer */
    if (sem_post(&shmb_p->sem_writer) == -1) {
        strerr_exit("handle_with_cache, sem_post (hdr)", errno);
    }

    gfs_sendheader(ctx, GF_OK, file_size);

    /* STEPS FOR FILE TRANSFER */
    /* We use a pair of semaphores to alternate control between reader (i.e. handle_with_cache)
    and the writer (i.e. simplecached). We also keep track of number of trnasfers and bytes
    transferred */
    int nxfrs = 0;
    ssize_t bytes_xfrd = 0;

    for (nxfrs = 0, bytes_xfrd = 0 ;; nxfrs++, bytes_xfrd += shmb_p->cnt) {

        /* Lock the reader semaphore. If it is already locked, this call will block. Thus, the
        control will never go past this point unless the writer has unlocked the semaphore. */
        if (sem_wait(&shmb_p->sem_reader) == -1) {
            strerr_exit("handle_with_cache, sem_wait (data)", errno);
        }

        /* File is done on the writer side */
        if (shmb_p->cnt == 0) {
            break;
        }

        /* Read Bytes from the shared memory */
        // printf("\n[handle_with_cache] writing data below \n\n");
        // write(STDOUT_FILENO, shm_buf, shmb_p->cnt);

        if (gfs_send(ctx, shm_buf, shmb_p->cnt) != shmb_p->cnt) {
            err_exit("handle_with_cache", "gfs_send", EXIT_FAILURE);
        }

        /* Unlock the writer */
        if (sem_post(&shmb_p->sem_writer) == -1) {
            strerr_exit("handle_with_cache, sem_post (data)", errno);
        }
    }

    /* Work is done; steps to put the shared memory segment back into queue */
    fprintf(stderr, "[handle_with_cache, thread_id = %ld] FF, about to lock mutex\n", tid);
    pthread_mutex_lock(wld->sq_mtx_p);
    fprintf(stderr, "[handle_with_cache, thread_id = %ld] Enqueuing the segnum %ld\n", tid, shm_segnum);
    steque_enqueue(wld->segqueue, (steque_item) shm_segnum);
    pthread_mutex_unlock(wld->sq_mtx_p);
    pthread_cond_broadcast(wld->sq_notempty_p);

    return bytes_xfrd;
}
ssize_t handle_with_cache(gfcontext_t* ctx, char *path, void* arg) {
        char* fldes;

        pthread_mutex_lock(&fldes_mutex);
        while (fldes_queue_size == 0) {
                pthread_cond_wait(&read_cond, &fldes_mutex);
        }
        fldes = (char*)steque_pop(&fldes_queue);
        fldes_queue_size--;
//		fprintf(stdout, "fldes is:%s.", fldes);
        pthread_mutex_unlock(&fldes_mutex);
        pthread_cond_broadcast(&write_cond);
		//pthread_cond_signal(&write_cond);



        shm_data* mem = create_shm_channel(fldes);

        mqd_t mq;

        do {
                mq = mq_open (MSGQID, O_RDWR);
        } while(mq == (mqd_t) -1 || mq == 0);

        msgq_data data;
        strcpy(data.path, path);
        strcpy(data.fldes, fldes);
        data.segment_size = segment_size;

 //       pthread_mutex_lock(&msgq_mutex);

        int msg_rsp = mq_send(mq, (const char *) &data, 1024, 1);
        if (msg_rsp < 0) {
                fprintf(stderr, "Error %d (%s) on server proxy mq_send.\n", errno, strerror(errno));
				fflush(stdout);
  //              exit(1);
        }
        mq_close(mq);
 //       pthread_mutex_unlock(&msgq_mutex);

        pthread_mutex_lock(&mem->file_len_mutex);
        while(mem->file_length == 0) {
			pthread_cond_wait(&mem->proxy_cond, &mem->file_len_mutex);
        }

        int file_len = mem->file_length;

        pthread_mutex_unlock(&mem->file_len_mutex);
        pthread_cond_broadcast(&mem->cache_cond);

 //       fprintf(stdout, "Received file length is: %d\n", file_len);
//        fflush(stdout);
        
        if(file_len < 0) {
 //               fprintf(stdout, "File not found in cache\n");
//				fflush(stdout);
                destroy_shm_seg(fldes, mem);
                return gfs_sendheader(ctx, GF_FILE_NOT_FOUND, 0);
        }else {

                gfs_sendheader(ctx, GF_OK, file_len);

             
                int bytes_transferred = 0;
                int write_len = 0;
                char *data_start = (void *)(mem + 1);
                while (bytes_transferred < file_len) {

                        pthread_mutex_lock(&mem->data_mutex);
                        while(mem->bytes_written == 0) {
                                pthread_cond_wait(&mem->proxy_cond, &mem->data_mutex);
                        }
                        int read_len = mem->bytes_written;
                        write_len = gfs_send(ctx, data_start, read_len);
                        if (write_len != read_len) {
                                fprintf(stderr, "handle_with_cache write error");
                                return EXIT_FAILURE;
                        }
                        mem->bytes_written = 0;

                        pthread_mutex_unlock(&mem->data_mutex);
                        pthread_cond_broadcast(&mem->cache_cond);
                        bytes_transferred += write_len;
                }


                destroy_shm_seg(fldes, mem);
                fflush(stdout);
                return bytes_transferred;
        }
}