public void OnMessage(Gs2SessionTaskId gs2SessionTaskId, Gs2Response gs2Response) { using (var scopedLock = new NonreentrantLock.ScopedLock(_lock)) { _gs2SessionTaskList.Find(v => v.Gs2SessionTaskId == gs2SessionTaskId)?.Complete(gs2Response); } }
protected void CloseCallback() { var isNowClosed = false; using (var scopedLock = new NonreentrantLock.ScopedLock(_lock)) { ProjectToken = null; if (_gs2SessionTaskList.Count > 0) { _state = State.Closed; CompleteGs2SessionTasks(new Gs2Response(new SessionNotOpenException("Session no longer open."))); } else { _state = State.Idle; isNowClosed = true; CompleteCloseTasks(); } } if (isNowClosed) { OnClose?.Invoke(); } }
protected void OpenCallback(string projectToken, Gs2Exception e) { if (projectToken == null && e == null) { // 応答からプロジェクトトークンが取得できなかった場合 // そのまま Idle 状態に遷移するので、 Open 失敗の後始末が必要な場合は派生クラスで対応が必要 e = new UnknownException("No project token returned."); } using (var scopedLock = new NonreentrantLock.ScopedLock(_lock)) { if (e == null) { ProjectToken = projectToken; _state = State.Available; CompleteOpenTasks(new AsyncResult <OpenResult>(new OpenResult(), null)); } else { // キャンセルがかけられていれば、実際の失敗の内容が何であれ、キャンセルによる失敗として扱う if (_state == State.CancellingOpen) { e = new SessionNotOpenException("Cancelled."); } _state = State.Idle; // TODO: Open と Close のコールバックの順番の保証 CompleteOpenTasks(new AsyncResult <OpenResult>(null, e)); CompleteCloseTasks(); } } }
// 実行するセッションとタスクは型の整合が取れている必要があるので、派生クラスで型を限定したインターフェースを公開する protected IEnumerator Execute(Gs2SessionTask gs2SessionTask) { var isNowClosed = false; using (var scopedLock = new NonreentrantLock.ScopedLock(_lock)) { if (_state == State.Available) { gs2SessionTask.Gs2SessionTaskId = _gs2SessionTaskIdGenerator.Issue(); _gs2SessionTaskList.Add(gs2SessionTask); var current = gs2SessionTask.Execute(this); using (var unlockScope = new NonreentrantLock.ScopedUnlock(_lock)) { yield return(current); } while (!gs2SessionTask.IsCompleted) { using (var unlockScope = new NonreentrantLock.ScopedUnlock(_lock)) { yield return(null); } } _gs2SessionTaskList.Remove(gs2SessionTask); if (_gs2SessionTaskList.Count == 0 && _state == State.Closed) { _state = State.Idle; isNowClosed = true; CompleteCloseTasks(); } ; } else { gs2SessionTask.Complete(new Gs2Response(new SessionNotOpenException("The session is not opened."))); } } gs2SessionTask.InvokeCallback(); if (isNowClosed) { OnClose?.Invoke(); } }
/// <summary> /// セッションのオープン操作をおこなうコルーチンを返却します。<br /> /// <br /> /// コルーチンの完了時にはコールバックが返っていることが保証されます。<br /> /// ただし、オープン状態のセッションは外部要因によって予期せずクローズされることがあるため、オープン操作成功のコールバックが返った時点でも、セッションが確実にオープン状態であることは保証されません。<br /> /// <br /> /// セッションのオープン操作は、オープン途中やすでにオープン状態のセッションに対しても重ねておこなうことができます。<br /> /// </summary> /// /// <returns>IEnumerator</returns> /// <param name="callback">コールバックハンドラ</param> public IEnumerator Open(OpenCallbackType callback) { var openTask = new OpenTask(callback); using (var scopedLock = new NonreentrantLock.ScopedLock(_lock)) { if (_state == State.Available) { openTask.Complete(new AsyncResult <OpenResult>(new OpenResult(), null)); } else { _openTaskList.Add(openTask); while (true) { if (openTask.IsCompleted) { break; } IEnumerator current = null; if (_state == State.Idle) { _state = State.Opening; if (Credential is BasicGs2Credential) { current = OpenImpl(); } else if (Credential is ProjectTokenGs2Credential) { using (var scopedUnlock = new NonreentrantLock.ScopedUnlock(_lock)) { OpenCallback(Credential.ProjectToken, null); } } } using (var scopedUnlock = new NonreentrantLock.ScopedUnlock(_lock)) { yield return(current); } } } } openTask.InvokeCallback(); }
/// <summary> /// セッションのクローズ操作をおこなうコルーチンを返却します。<br /> /// <br /> /// コルーチンの完了時にはコールバックが返っていることが保証されます。<br /> /// また、セッションのクローズ操作は必ず成功し、コールバックが返った時点でセッションがクローズド状態であり、そのセッションを利用していたすべての API の実行が完了していることが保証されます。<br /> /// ただし、複数のクローズ操作を同時に実行し、そのコールバックでオープン操作を開始した場合はその限りではありません。<br /> /// <br /> /// セッションに対して複数回のオープン操作をおこなっていた場合であっても、1回のクローズ操作でクローズド状態へ移行します。<br /> /// </summary> /// /// <returns>IEnumerator</returns> /// <param name="callback">コールバックハンドラ</param> public IEnumerator Close(CloseCallbackType callback) { var closeTask = new CloseTask(callback); var isNowClosed = false; using (var scopedLock = new NonreentrantLock.ScopedLock(_lock)) { if (_state == State.Idle) { closeTask.Complete(); } else { _closeTaskList.Add(closeTask); while (true) { if (closeTask.IsCompleted) { break; } IEnumerator current = null; switch (_state) { case State.Opening: _state = State.CancellingOpen; current = CancelOpenImpl(); break; case State.Available: ProjectToken = null; if (_gs2SessionTaskList.Count == 0) { _state = State.Closing; current = CloseImpl(); } else { _state = State.CancellingTasks; foreach (var gs2SessionTask in _gs2SessionTaskList) { gs2SessionTask.Cancel(); } } break; case State.CancellingTasks: if (_gs2SessionTaskList.Count == 0) { _state = State.Closing; current = CloseImpl(); } break; case State.Closed: if (_gs2SessionTaskList.Count == 0) { _state = State.Idle; isNowClosed = true; CompleteCloseTasks(); continue; } break; case State.Idle: case State.CancellingOpen: case State.Closing: break; } using (var unlockScope = new NonreentrantLock.ScopedUnlock(_lock)) { yield return(current); } } } } if (isNowClosed) { OnClose?.Invoke(); } closeTask.InvokeCallback(); }