/// <summary>
        /// Call if one worker is available
        /// </summary>
        /// <param name="workerUrl">worker url</param>
        /// <returns></returns>
        public bool FinishProcessing(string workerUrl, WorkStatus workerStatus)
        {
            TestFrozenC();
            jobsAssigmentMutex.WaitOne();

            foreach (WorkStatus iWorkStatus in jobsAssigment)
            {
                if (iWorkStatus.getWorkerId() == workerStatus.getWorkerId() && iWorkStatus.getBeginIndex() == workerStatus.getBeginIndex())
                {
                    iWorkStatus.setLastModification(workerStatus.getLastModification());
                    iWorkStatus.setNumberLinesComputed(workerStatus.getNumberLinesComputed());
                    iWorkStatus.setTotalNumberOfLines(workerStatus.getTotalNumberLines());
                    iWorkStatus.setWorkerId(workerStatus.getWorkerId());

                    //Console.WriteLine("FINISH " + workerStatus.getWorkerId());
                    break;
                }
            }

            jobsAssigmentMutex.ReleaseMutex();

            availableWorkerToJobMutex.WaitOne();
            bool exists = availableWorkerToJob.Contains(workerUrl);

            availableWorkerToJobMutex.ReleaseMutex();

            if (!exists)
            {
                availableWorkerToJobMutex.WaitOne();
                availableWorkerToJob.Add(workerUrl);
                availableWorkerToJobMutex.ReleaseMutex();

                queueWorkersSemaphore.Release(1);
            }



            return(true);
        }
        /// <summary>
        /// Distributes the split by the workers as new workers are available
        /// </summary>
        /// <param name="className">ClassName to run</param>
        /// <param name="dllCode">Class dll to run</param>
        /// <param name="clientUrl">Client Url to communicate answer</param>
        public void DistributeWorkAsync(string className, byte[] dllCode, string clientUrl)
        {
            while (!jobDone)
            {
                TestFrozenC();

                //Get first job available

                jobsAssigmentMutex.WaitOne();


                //All jobs concluded
                bool isCompleted = true;

                foreach (WorkStatus iWorkStatus in jobsAssigment)
                {
                    if (!iWorkStatus.isWorkCompleted())
                    {
                        isCompleted = false;
                        break;
                    }
                }

                if (isCompleted)
                {
                    jobsAssigmentMutex.ReleaseMutex();
                    jobDone = true;
                    break;
                }


                //Still work to do
                WorkStatus freeWorkStatus = null;

                foreach (WorkStatus iWorkStatus in jobsAssigment)
                {
                    //Console.WriteLine(iWorkStatus.isWorkerAssigned() + " => " + iWorkStatus.getBeginIndex());

                    if (!iWorkStatus.isWorkerAssigned())
                    {
                        freeWorkStatus = iWorkStatus;
                        break;
                    }
                }

                //No more works to be done
                if (freeWorkStatus == null)
                {
                    jobsAssigmentMutex.ReleaseMutex();
                    continue;
                }

                //temporary assignment
                freeWorkStatus.setWorkerId(-2);
                long freeWorkStatusBeginIndex = freeWorkStatus.getBeginIndex();
                jobsAssigmentMutex.ReleaseMutex();

                //Get first free worker
                queueWorkersSemaphore.WaitOne();

                //Send the job to worker
                availableWorkerToJobMutex.WaitOne();

                String workerUrl = (string)availableWorkerToJob[0];
                availableWorkerToJob.RemoveAt(0);
                availableWorkerToJobMutex.ReleaseMutex();

                if (workerUrl == null)
                {
                    throw new ArgumentNullException();
                }



                //System.Console.WriteLine("[DISTRIBUTE_WORK] Sending job to " + workerUrl + " with split [" + freeWorkStatus.getBeginIndex() + ", " + freeWorkStatus.getEndIndex() + "] ... ");

                IWorker workerObj = (IWorker)Activator.GetObject(typeof(IWorker), workerUrl);


                try {
                    //Get id from url
                    int workerID = 0;

                    //Get id by url
                    workersIDUrlMutex.WaitOne();
                    foreach (KeyValuePair <int, string> workerPair in workersIDUrl)
                    {
                        if (workerPair.Value == workerUrl)
                        {
                            workerID = workerPair.Key;

                            break;
                        }
                    }
                    workersIDUrlMutex.ReleaseMutex();

                    //Assign that job to this worker
                    jobsAssigmentMutex.WaitOne();

                    foreach (WorkStatus ws in jobsAssigment)
                    {
                        if (freeWorkStatusBeginIndex == ws.getBeginIndex())
                        {
                            freeWorkStatus = ws;
                        }
                    }

                    freeWorkStatus.setWorkerId(workerID);
                    workerObj.RunJob(className, dllCode, freeWorkStatus.getBeginIndex(), freeWorkStatus.getEndIndex(), clientUrl, ownUrl);



                    jobsAssigmentMutex.ReleaseMutex();
                } catch (Exception e) {
                    System.Console.WriteLine("[ERROR_DISTRIBUTE_WORK1]  Cant send work to " + workerUrl);
                    jobsAssigmentMutex.WaitOne();
                    freeWorkStatus.removeWorker();
                    jobsAssigmentMutex.ReleaseMutex();

                    availableWorkerToJobMutex.WaitOne();
                    availableWorkerToJob.Add(workerUrl);
                    availableWorkerToJobMutex.ReleaseMutex();

                    queueWorkersSemaphore.Release(1);
                }
            }
        }
        // =============================================
        // Threads Section
        // =============================================

        /// <summary>
        /// Async method to keep tracking of alive workers
        /// </summary>
        public void IsAliveAsync()
        {
            while (!jobDone)
            {
                TestFrozenC();

                //Is alive interval
                Thread.Sleep(2000);

                //Create a copy of the workersUrl
                workersIDUrlMutex.WaitOne();
                HashSet <KeyValuePair <int, string> > clonedWorkersUrl = new HashSet <KeyValuePair <int, string> >(workersIDUrl);
                workersIDUrlMutex.ReleaseMutex();

                jobsAssigmentMutex.WaitOne();
                HashSet <WorkStatus> clonedJobAssigments = new HashSet <WorkStatus>(jobsAssigment);
                jobsAssigmentMutex.ReleaseMutex();

                foreach (KeyValuePair <int, string> worker in clonedWorkersUrl)
                {
                    //Get the worker
                    IWorker workerObj = (IWorker)Activator.GetObject(typeof(IWorker), worker.Value);

                    try {
                        //Retrive the workStatus of the worker
                        WorkStatus workerStatus = workerObj.IsAlive(clonedWorkersUrl, clonedJobAssigments);


                        //Update the workStatus of that worker

                        jobsAssigmentMutex.WaitOne();
                        foreach (WorkStatus iWorkStatus in jobsAssigment)
                        {
                            if (iWorkStatus.getWorkerId() == workerStatus.getWorkerId() && iWorkStatus.getBeginIndex() == workerStatus.getBeginIndex())
                            {
                                iWorkStatus.setLastModification(workerStatus.getLastModification());
                                iWorkStatus.setNumberLinesComputed(workerStatus.getNumberLinesComputed());
                                iWorkStatus.setTotalNumberOfLines(workerStatus.getTotalNumberLines());
                                iWorkStatus.setWorkerId(workerStatus.getWorkerId());

                                //Console.WriteLine("isalive"  + workerStatus.getWorkerId());

                                break;
                            }
                        }
                        jobsAssigmentMutex.ReleaseMutex();

                        //TODO: test if the worker is too slow
                    } catch (Exception) { //The worker didn't anwser
                        Console.WriteLine("***************" + worker.Value + "dead");

                        //Remove the worker from the workersList
                        workersIDUrlMutex.WaitOne();
                        workersIDUrl.Remove(worker);
                        workersIDUrlMutex.ReleaseMutex();

                        //Removes all the works not done and assigned to it
                        jobsAssigmentMutex.WaitOne();
                        foreach (WorkStatus iWorkStatus in jobsAssigment)
                        {
                            if (iWorkStatus.getWorkerId() == worker.Key && !iWorkStatus.isWorkCompleted())
                            {
                                iWorkStatus.removeWorker();
                            }
                        }
                        jobsAssigmentMutex.ReleaseMutex();


                        //Remove from available worker
                        availableWorkerToJobMutex.WaitOne();
                        availableWorkerToJob.Remove(worker.Value);
                        availableWorkerToJobMutex.ReleaseMutex();
                    }
                }
            }
        }