#include <iostream>

#include <tbb/task_scheduler_init.h>
#include <tbb/parallel_for.h>
#include <tbb/blocked_range.h>
#include <tbb/queuing_mutex.h>

#define N 40000000

using namespace std;
using namespace tbb;

struct node {
	int data;
	node *next;
};

class par_stack {
	private:
		node *top;
		queuing_mutex smtx;

	public:
		par_stack()
		{
			top = NULL;
			cout << "Created a new parallel stack!" << endl;
		}

		bool empty()
		{
			return top == NULL;
		}

		bool push( const int &x )
		{
			node *new_node = new node;

			if ( new_node != NULL ) {
				new_node->data = x;
				new_node->next = top;
				top = new_node;
				return true;
			} else {
				return false;
			}
		}

		void print()
		{
			for ( node *tmp = top; tmp != NULL; tmp = tmp->next ) {
				cout << tmp->data << " ";
			}
			cout << endl;
		}

		bool pop( int &_e ) {
			 node* tp = NULL;
			 if ( ! top ) {
				 goto done;
			 }

			 {
				  queuing_mutex::scoped_lock lock( smtx );

				  if ( ! top ) {
					  goto done;
				  }

				  tp = top;
				  top = top->next;
			 }
			 _e = tp->data;
			 delete tp;
		done:
			 return tp!=NULL;
		}

};

class parallel_pop {
	private:
		par_stack &stk;

	public:
		parallel_pop( par_stack &_stk) : stk(_stk) {}

		void operator() ( const blocked_range<int> &r) const {
			int x;

			printf("Thread: %d - %d\n", r.begin(), r.end());

			for ( int i=r.begin(); i!=r.end(); i++) {
				if ( ! stk.pop( x ) ) {
					cout << "Stack is empty " << endl;
				}
			}
		}
};

int main()
{
	task_scheduler_init init(4);

	par_stack stk;

	for ( int i=0; i<N; i++ ) {
		stk.push(i);
	}

	cout << "Now deleting ..." << endl;

	parallel_for(blocked_range<int> (0,N+10,N/4), parallel_pop( stk ));

	return 0;
}

