public void Enqueue(TaskWorker new_worker) { bool start = false; lock ( _sync ) { Queue work_queue = (Queue)_task_to_workers[new_worker.Task]; if (work_queue == null) { //This is a new task: work_queue = new Queue(); _task_to_workers[new_worker.Task] = work_queue; //Start the job! start = true; } //In any case, add the worker: new_worker.FinishEvent += this.TaskEndHandler; work_queue.Enqueue(new_worker); _worker_count++; } /* * Get to work! */ if (start && (1 == _is_active)) { Start(new_worker); } }
/** * This creates a TaskWorker that represents the next step that should * be taken for the ta. It can only be two tasks: create the edge * (EdgeWorker) or wait and try again (RestartState). * * We return null if: * - the TA is null * - Linker is finished * - a Connection was already created * - this TA has been restarted too many times * * If we cannot get a ConnectionTable.Lock with SetTarget, we return a * RestartState to wait a little while to try to get the lock again. * * @returns the next TaskWorker that should be enqueued, does not start or * Enqueue it. */ protected BC.TaskWorker StartAttempt(TransportAddress next_ta) { BC.TaskWorker next_task = null; if ((next_ta == null) || (_added_cons != 0) || IsFinished || ConnectionInTable) { //Looks like we are already connected... return(null); } try { #if LINK_DEBUG if (BU.ProtocolLog.LinkDebug.Enabled) { BU.ProtocolLog.Write(BU.ProtocolLog.LinkDebug, String.Format("{0}: Linker ({1}) attempting to lock {2}", _local_n.Address, _lid, _target)); } #endif /* * If we cannot set this address as our target, we * stop before we even try to make an edge. * * Locks flow around in complex ways, but we * (or one of our LinkProtocolState) * will hold the lock */ SetTarget(); #if LINK_DEBUG if (BU.ProtocolLog.LinkDebug.Enabled) { BU.ProtocolLog.Write(BU.ProtocolLog.LinkDebug, String.Format("{0}: Linker ({1}) acquired lock on {2}", _local_n.Address, _lid, _target)); BU.ProtocolLog.Write(BU.ProtocolLog.LinkDebug, String.Format("{0}: Linker: ({1}) Trying TA: {2}", _local_n.Address, _lid, next_ta)); } #endif next_task = new EdgeWorker(_local_n, next_ta); next_task.FinishEvent += this.EdgeWorkerHandler; } catch (CTLockException) { /* * If we cannot get a lock on the address in SetTarget() * we wait and and try again */ #if LINK_DEBUG if (BU.ProtocolLog.LinkDebug.Enabled) { BU.ProtocolLog.Write(BU.ProtocolLog.LinkDebug, String.Format("{0}: Linker ({1}) failed to lock {2}", _local_n.Address, _lid, _target)); } #endif next_task = GetRestartState(next_ta); } catch (ConnectionExistsException) { //We already have a connection to the target } catch (Exception) { } return(next_task); }
protected override void Start(BC.TaskWorker tw) { try { LocalNode.EnqueueAction(tw); } catch { /* * We could get an exception if queue in LocalNode is closed */ tw.Start(); } }
////////////////// /// /// Protected/Private methods /// ///////////////// /** * @param success if this is true, we have a new edge to try else, make a new edge * @param target_ta the transport address this edge was created with * @param e the new edge, if success * @param x the exception which may be present if sucess is false */ protected void EdgeWorkerHandler(object edgeworker, EventArgs args) { EdgeWorker ew = (EdgeWorker)edgeworker; bool close_edge = false; BC.TaskWorker next_task = null; try { Edge e = ew.NewEdge; //This can throw an exception SetTarget(); //This can also throw an exception //If we make it here, we did not have any problem. next_task = new LinkProtocolState(this, ew.TA, e); next_task.FinishEvent += this.LinkProtocolStateFinishHandler; } catch (LinkException) { //This happens if SetTarget sees that we are already connected //Our only choice here is to close the edge and give up. close_edge = true; } catch (EdgeException) { /* * If there is some problem creating the edge, * we wind up here. Just move on */ next_task = StartAttempt(NextTA()); } catch (Exception ex) { /* * The edge creation didn't work out so well */ BU.ProtocolLog.WriteIf(BU.ProtocolLog.LinkDebug, ex.ToString()); next_task = StartAttempt(NextTA()); } if (close_edge) { try { ew.NewEdge.Close(); } catch (Exception) { //Ignore any exception } } if (next_task != null) { /* * We should start a new task now */ _task_queue.Enqueue(next_task); } }
///////////// /// /// Public methods /// /////////// /** * This tells the Linker to make its best effort to create * a connection to another node */ override public void Start() { #if LINK_DEBUG if (BU.ProtocolLog.LinkDebug.Enabled) { BU.ProtocolLog.Write(BU.ProtocolLog.LinkDebug, String.Format("{0}, Linker({1}).Start at: {2}", _local_n.Address, _lid, DateTime.UtcNow)); } #endif //Try to set _started to 1, if already set to one, throw an exception if (Interlocked.Exchange(ref _started, 1) == 1) { throw new Exception("Linker already Started"); } //Just move to the next (first) TA //Get the set of addresses to try int parallel_attempts = _MAX_PARALLEL_ATTEMPTS; if (_target == null) { //Try more attempts in parallel to get leaf connections. //This is a hack to make initial connection faster parallel_attempts = 2 * parallel_attempts; } //This would be an ideal place for a list comprehension ArrayList tasks_to_start = new ArrayList(parallel_attempts); for (int i = 0; i < parallel_attempts; i++) { BC.TaskWorker t = StartAttempt(NextTA()); if (t != null) { tasks_to_start.Add(t); } } foreach (BC.TaskWorker t in tasks_to_start) { _task_queue.Enqueue(t); } /* * We have so far prevented ourselves from sending the * FinishEvent. Now, we have laid all the ground work, * if there are no active tasks, there won't ever be, * so lets check to see if we need to fire the finish * event */ Interlocked.Exchange(ref _hold_fire, 0); if (_task_queue.WorkerCount == 0) { FinishCheckHandler(_task_queue, EventArgs.Empty); } }
/** * When a TaskWorker completes, we remove it from the queue and * start the next in that task queue */ protected void TaskEndHandler(object worker, EventArgs args) { TaskWorker new_worker = null; EventHandler eh = null; lock ( _sync ) { TaskWorker this_worker = (TaskWorker)worker; object task = this_worker.Task; Queue work_queue = (Queue)_task_to_workers[task]; if (work_queue != null) { work_queue.Dequeue(); if (work_queue.Count > 0) { //Now the new job is at the head of the queue: new_worker = (TaskWorker)work_queue.Peek(); } else { /* * There are no more elements in the queue, forget it: * If we leave a 0 length queue, this would be a memory * leak */ _task_to_workers.Remove(task); } _worker_count--; if (_worker_count == 0) { eh = EmptyEvent; } } else { //This TaskEndHandler has been called more than once clearly. Console.Error.WriteLine("ERROR: {0} called TaskEndHandler but no queue for this task: {1}", worker, task); } } if (new_worker != null && (1 == _is_active)) { //You start me up! Start(new_worker); } if (eh != null) { eh(this, EventArgs.Empty); } }
/** * When a RestartState finishes its task, this is the * EventHandler that is called. * * At the end of a RestartState, we call StartAttempt for * the TA we are waiting on. If we have restarted too many * times, we move to the next TA, and StartAttempt with that one. */ protected void RestartHandler(object orss, EventArgs args) { RestartState rss = (RestartState)orss; BC.TaskWorker next_task = StartAttempt(rss.TA); if (next_task == null) { //Looks like it's time to move on: next_task = StartAttempt(NextTA()); } if (next_task != null) { _task_queue.Enqueue(next_task); } }
/** * If you want to control if new TaskWorkers are started in some * other thread, or event loop, you can override this method */ protected virtual void Start(TaskWorker tw) { tw.Start(); }
public void Enqueue(TaskWorker new_worker) { bool start = false; lock( _sync ) { Queue work_queue = (Queue)_task_to_workers[ new_worker.Task ]; if( work_queue == null ) { //This is a new task: work_queue = new Queue(); _task_to_workers[ new_worker.Task ] = work_queue; //Start the job! start = true; } //In any case, add the worker: new_worker.FinishEvent += this.TaskEndHandler; work_queue.Enqueue(new_worker); _worker_count++; } /* * Get to work! */ if( start && (1 == _is_active)) { Start(new_worker); } }
protected void LinkProtocolStateFinishHandler(object olps, EventArgs args) { LinkProtocolState lps = (LinkProtocolState)olps; #if LINK_DEBUG if (BU.ProtocolLog.LinkDebug.Enabled) { BU.ProtocolLog.Write(BU.ProtocolLog.LinkDebug, String.Format("{0}: Linker({1}): {2} finished with result: {3} at: {4}", _local_n.Address, _lid, lps, lps.MyResult, DateTime.UtcNow)); } #endif BC.TaskWorker next_task = null; switch (lps.MyResult) { case LinkProtocolState.Result.Success: /* * Great, the Connection is up and in our table now * Just do nothing now and wait for the other tasks * to finish, at which point, the Linker will fire * its FinishEvent. */ Interlocked.Increment(ref _added_cons); #if LINK_DEBUG if (BU.ProtocolLog.LinkDebug.Enabled) { BU.ProtocolLog.Write(BU.ProtocolLog.LinkDebug, String.Format("{0}: Linker({1}) added {2} at: {3}", _local_n.Address, _lid, lps.Connection, DateTime.UtcNow)); } #endif break; case LinkProtocolState.Result.RetryThisTA: next_task = GetRestartState(lps.TA); if (next_task == null) { goto case LinkProtocolState.Result.MoveToNextTA; } break; case LinkProtocolState.Result.MoveToNextTA: //Hold the lock, it will be transferred: // old LPS -> Linker -> new LPS next_task = StartAttempt(NextTA()); break; case LinkProtocolState.Result.ProtocolError: break; case LinkProtocolState.Result.Exception: break; default: //This should not happen. Console.Error.WriteLine("unrecognized result: " + lps.MyResult.ToString()); break; } if (next_task != null) { //We have some new task to start _task_queue.Enqueue(next_task); } int current_active = Interlocked.Decrement(ref _active_lps_count); if (current_active == 0) { //We have finished handling this lps finishing, //if we have not started another yet, we are not //going to right away. In the mean time, release //the lock Unlock(); } }
////////////////// /// /// Protected/Private methods /// ///////////////// /** * @param success if this is true, we have a new edge to try else, make a new edge * @param target_ta the transport address this edge was created with * @param e the new edge, if success * @param x the exception which may be present if sucess is false */ protected void EdgeWorkerHandler(object edgeworker, EventArgs args) { EdgeWorker ew = (EdgeWorker)edgeworker; bool close_edge = false; BC.TaskWorker next_task = null; try { Edge e = ew.NewEdge; //This can throw an exception SetTarget(); //This can also throw an exception //If we make it here, we did not have any problem. next_task = new LinkProtocolState(this, ew.TA, e); next_task.FinishEvent += this.LinkProtocolStateFinishHandler; //Keep a proper track of the active LinkProtocolStates: Interlocked.Increment(ref _active_lps_count); } catch (ConnectionExistsException) { //We already have a connection to the target close_edge = true; } catch (LinkException) { //This happens if SetTarget sees that we are already connected //Our only choice here is to close the edge and give up. close_edge = true; } catch (CTLockException) { /* * SetTarget could not get the lock on the address. * Try again later */ close_edge = true; next_task = GetRestartState(ew.TA); if (next_task == null) { //We've restarted too many times: next_task = StartAttempt(NextTA()); } } catch (EdgeException) { /* * If there is some problem creating the edge, * we wind up here. Just move on */ next_task = StartAttempt(NextTA()); } catch (Exception ex) { /* * The edge creation didn't work out so well */ BU.ProtocolLog.WriteIf(BU.ProtocolLog.LinkDebug, ex.ToString()); next_task = StartAttempt(NextTA()); } if (close_edge) { try { ew.NewEdge.Close(); } catch (Exception) { //Ignore any exception } } if (next_task != null) { /* * We should start a new task now */ _task_queue.Enqueue(next_task); } }
protected override void Start(TaskWorker tw) { LocalNode.EnqueueAction(tw); }