void destroy_ConnectionPool(void) {
     // pop until queue is empty
     photodb_client client;
     while (!client_queue.size() != 0) {
         client_queue.pop(client, 1, 0);
     }
     cout << "release OK" << endl;
 }
void *ExporterSink::exporterSinkProcess(void *arg)
{
	ExporterSink *sink = (ExporterSink *)arg;
	ConcurrentQueue<Packet*> *queue = sink->getQueue();
	Packet *p;
	bool result;
	// our deadline
	struct timeval deadline;
	int pckCount;

	msg(MSG_INFO, "Sink: now running ExporterSink thread");

	while(!sink->exitFlag) {
		sink->startNewPacketStream();

		// let's get the first packet
		gettimeofday(&deadline, 0);
		
		result = queue->pop(&p);

	    if(result == true) {
			// we got a packet, so let's add the record
			result = sink->addPacket(p);
		}
			
		pckCount = 1;

		// now calculate the deadline by which the packet has to leave the exporter
		gettimeofday(&deadline, 0);
		deadline.tv_usec += sink->exportTimeout * 1000L;
		if(deadline.tv_usec > 1000000L) {
			deadline.tv_sec += (deadline.tv_usec / 1000000L);
			deadline.tv_usec %= 1000000L;
		}

		while(!sink->exitFlag && (pckCount < sink->ipfix_maxrecords)) {
			// Try to get next packet from queue before our deadline
			result = queue->popAbs(deadline, &p);

			// check for timeout and break loop if neccessary
			if (!result) break;

			// no timeout received, continue waiting, but

			// count only if packet was added
			if(sink->addPacket(p) == true)
			    pckCount++;
		}
		sink->flushPacketStream();
	}
	return 0;
}
// 模仿java.util.concurrent库并发容器及Mircosoft的并发容器而写的一个简单并发队列
// http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html
// http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/LinkedBlockingQueue.html
// http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html
// http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/PriorityBlockingQueue.html
int main()
{
    {
        FifoConcurrentQueue<int> cq;
        cq.push(3);
        cq.push(1);
        cq.push(2);

        int i;
        cq.pop(i);
        assert(i == 3);
        cq.pop(i);
        assert(i == 1);
        cq.pop(i);
        assert(i == 2);
    }

    {
        FiloConcurrentQueue<int> cq;
        cq.push(3);
        cq.push(1);
        cq.push(2);

        int i;
        cq.pop(i);
        assert(i == 2);
        cq.pop(i);
        assert(i == 1);
        cq.pop(i);
        assert(i == 3);
    }

    {
        PrioConcurrentQueue<int> cq;
        cq.push(3);
        cq.push(1);
        cq.push(2);

        int i;
        cq.pop(i);
        assert(i == 3);
        cq.pop(i);
        assert(i == 2);
        cq.pop(i);
        assert(i == 1);
    }
    {
        ConcurrentQueue<int, std::priority_queue<int, std::vector<int>, std::greater<int> >, ConcurrentQueueTraits::tagPRIO> cq;
        cq.push(3);
        cq.push(1);
        cq.push(2);

        int i;
        cq.pop(i);
        assert(i == 1);
        cq.pop(i);
        assert(i == 2);
        cq.pop(i);
        assert(i == 3);
    }

}
int main(int argc, char *argv[]) {
	// read config.ini file
	Config config;
	auto nthreads  = config.get<int>("nthreads");
	auto device    = config.get<const char *>("disk_guest");
	auto traceFile = config.get<const char *>("trace_file");
	auto logDir    = config.get<string>("log_dir");
	
	// parse arg, prioritize argv over config
	if (argc > 1) traceFile = argv[1]; 
	if (argc > 2) nthreads = atoi(argv[2]);
	if (strstr(device, "/dev/sda")) { // avoid accidentally writing to system part 
		fprintf(stderr, "Error trying to write to system partition %s\n", device);
		return 1;
	}
	// use default value if not supplied
	if (strcmp(device, "") == 0) device = DEFAULT_DEVICE;
	if (strcmp(traceFile, "") == 0) traceFile = DEFAULT_TRACE_FILE;
	if (strcmp(logDir.c_str(), "") == 0) logDir = DEFAULT_LOG_DIR;
	if (nthreads == 0) nthreads = DEFAULT_NTHREADS;
	
	srand(time(NULL)); 	// initialize seed

	// print configuration
	printf("trace     : %s\n", traceFile);
	printf("nthreads  : %d\n", nthreads);
	printf("device    : %s\n", device);
	printf("log       : %s\n", logDir.c_str());
	printf("precision : %fms\n", Timer::getResolution());

	printf("Opening device %s\n", device);
	int fd = open(device, O_DIRECT | O_RDWR | O_SYNC); 
	if (fd < 0) {
		fprintf(stderr, "Error opening device '%s'\n", device);
		return 1;
	}

	printf("Allocating buffer\n");
	void *buf; 
	if (posix_memalign(&buf, MEM_ALIGN, LARGEST_REQUEST_SIZE * BYTE_PER_BLOCK)) {
		fprintf(stderr, "Error allocating buffer\n");
		return 1;
	}
	//memset(buf, rand() % 256, LARGEST_REQUEST_SIZE * BYTE_PER_BLOCK);

	printf("Opening trace file\n");
	TraceReader trace(traceFile); // open trace file
	ConcurrentQueue<TraceEvent> queue; // queue of trace events
	bool readDone = false; // whether or not we're done reading trace file
	
	printf("Start reading trace\n");
	thread fileThread([&] { // thread to read trace file 
		TraceEvent event;
		while (trace.read(event)) {
			event.time = event.time * 1000; // to microseconds
			event.size = event.bcount * BYTE_PER_BLOCK;
			queue.push(event);
		}
		readDone = true; 
		queue.notifyAll(); // notify worker we're done  
	});	
	queue.waitUntilFull(); // wait until at least queue's full

	printf("Start replaying trace\n");
	vector<thread> workers(nthreads); // generate worker threads
	atomic<int> lateCount(0), threadId(0); // late I/O count and threadId
	for (auto& t : workers) t = thread([&] { // launch workers 
		int myId = ++threadId; // id for this thread
		int myLateCount = 0; // local lateCount for this thread 
		Logger logger(logDir + traceFile + to_string(myId));
		
		Timer timer; // mark the beginning of worker thread	
		while (!readDone or !queue.empty()) { 
			TraceEvent event;
			if (not queue.pop(event)) continue; // retry 
			long currentTime = timer.elapsedTime(), nextIoTime = event.time;
			if (currentTime <= nextIoTime) { // we're early/on-time
				//printf(". next=%ld current=%ld \n", nextIoTime, currentTime);
				Timer::delay(nextIoTime - currentTime); // delay until ~specified time
			} else { // we're late
				//printf("x next=%ld current=%ld delta=%ld\n", 
				// nextIoTime, currentTime, currentTime-nextIoTime);
				++myLateCount;
			}
			
			performIo(fd, buf, event, logger);
		}
		lateCount += myLateCount; // update global lateCount
	});

	fileThread.join(); // wait for all threads to finish
	for (auto& t : workers) t.join(); 

	printf("Late count: %d\n", lateCount.load());
	Logger logger(logDir + traceFile + to_string(0));
	logger.printf("%d\n", lateCount.load());
	printf("Done\n");
	return 0;
}
    return_value *kv_up_get(unsigned long key_get, unsigned long size) {
        try {
            return_value *value_return = new return_value;

            photodb_client *client = new photodb_client;

            // wait 2 seconds 
            client_queue.pop(*client, 1, 2);
            // get metadata
            MetaValueResult metadata_result;
            try {
                client->metadata->getMeta(metadata_result, (long int) key_get); // Should check error
                if (metadata_result.error != 0) {
                    client_queue.put(*client, 1, 0);
                    delete value_return;
                    return NULL;
                }
            } catch (TException& tx) {
                cout << "ERROR GET META: " << tx.what() << endl;
                client_queue.put(*client, 1, 0);
                delete value_return;
                return NULL;
            }

            // get content of image
            ImgValueResult content_result;
            try {
                client->content->getImg(content_result, (long int) key_get, (int) size);
                if (content_result.error != 0) {
                    client_queue.put(*client, 1, 0);
                    delete value_return;
                    return NULL;
                }
            } catch (TException& tx) {
                cout << "ERROR GET META: " << tx.what() << endl;
                client_queue.put(*client, 1, 0);
                delete value_return;
                return NULL;
            }

            client_queue.put(*client, 1, 0);

            char *writable_content = new char[content_result.value.img.size() + 1];
            std::copy(content_result.value.img.begin(), content_result.value.img.end(), writable_content);
            writable_content[content_result.value.img.size() + 1] = '\0';
            // Prepare date to return
            value_return->content = writable_content;
            value_return->size = content_result.value.img.size();

            char *writable_etag = new char[metadata_result.value.etag.size() + 1];
            std::copy(metadata_result.value.etag.begin(), metadata_result.value.etag.end(), writable_etag);
            writable_etag[metadata_result.value.etag.size() + 1] = '\0';
            // Prepare etag to return
            value_return->etag = writable_etag;
            value_return->etag_size = metadata_result.value.etag.size();

            char *writable_contentType = new char[metadata_result.value.contentType.size() + 1];
            std::copy(metadata_result.value.contentType.begin(), metadata_result.value.contentType.end(), writable_contentType);
            writable_contentType[metadata_result.value.contentType.size()] = '\0';
            value_return->contentType = writable_contentType;
            value_return->content_type_size = metadata_result.value.contentType.size();

            return value_return;
        } catch (const exception& e) {
            cerr << "EXCEPTION: " << e.what() << endl;
            return NULL;
        }
    }