Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Review #4

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions Squared/Threading/Future.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public FutureHandlerException (IFuture future, Delegate handler, string message)
}

internal static class FutureHelpers {
// REVIEW: Pretty sure I would like to rewrite this RunInThreadThunk subsystem but not 100% sure how
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's old code so it probably does need it

public static readonly WaitCallback RunInThreadHelper;

static FutureHelpers () {
Expand All @@ -75,6 +76,7 @@ internal static void _RunInThreadHelper (object state) {
try {
thunk.Invoke();
} catch (System.Reflection.TargetInvocationException ex) {
// REVIEW: Which cases is this catch block for?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, I don't remember. I know Delegate.Invoke always throws TargetInvocationException so that might be why it's doing this unwrap here. It definitely needed a comment.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I have tried to convince Delegate.DynamicInvoke to trigger this exception but I mostly succeeded in it raising "other" exception types... (and that it would not make sense to unwrap to the client)

thunk.Fail(ex.InnerException);
} catch (Exception ex) {
thunk.Fail(ex);
Expand Down Expand Up @@ -154,14 +156,14 @@ Type ResultType {
void RegisterHandlers (OnFutureResolved completeHandler, OnFutureResolved disposeHandler);
void RegisterHandlers (OnFutureResolvedWithData completeHandler, OnFutureResolvedWithData disposeHandler, object userData);
void RegisterOnResolved (Action handler);
void RegisterOnResolved (OnFutureResolved handler);
void RegisterOnResolved (OnFutureResolvedWithData handler, object userData);
void RegisterOnComplete (Action handler);
void RegisterOnComplete (OnFutureResolved handler);
void RegisterOnComplete (OnFutureResolvedWithData handler, object userData);
void RegisterOnDispose (Action handler);
void RegisterOnDispose (OnFutureResolvedWithData handler, object userData);
void RegisterOnResolved (OnFutureResolved handler);
void RegisterOnComplete (OnFutureResolved handler);
void RegisterOnDispose (OnFutureResolved handler);
void RegisterOnDispose (OnFutureResolvedWithData handler, object userData);
void RegisterOnErrorCheck (Action handler);
bool CopyFrom (IFuture source);
}
Expand Down Expand Up @@ -190,6 +192,8 @@ public SignalFuture (bool signaled)

public static class Future {
public enum State : int {
// REVIEW: I guess Unknown and Disposing are removed because
// you did not want these states to be part of the public API?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I don't want the consumer to look at those and try to do something based on seeing them, though it might make sense to just broadcast them as 'Indeterminate' or something and say you should treat those as a 'please retry later'

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that, as both State_Indeterminate and State_Disposing are transitory, not exposing them is what makes the most sense for everyone (in particular, State_Indeterminate should really be understood as "This Future instance is locked with regard to operations that could resolve it", but no client that is outside of Future should treat it differently from "not resolved yet", which is Empty).

However, I guess that, given the existence of the various boolean properties (Resolved/Completed/Disposed/CompletedSuccessfully/CompletedWithError), I am not 100% sure that this enum needs to be public?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Of course, I am not saying that it should now be removed - that would break existing clients for no good reason)

Empty = 0,
// Unknown = 1,
CompletedWithValue = 2,
Expand Down Expand Up @@ -254,6 +258,10 @@ private sealed class WaitForFirstThunk {
public WaitForFirstThunk (IEnumerable<IFuture> futures) {
OnFutureResolved handler = null;

// REVIEW: On a theoretical level, I am worried that, if the enumerable is
// empty and not detected by the condition below, or if all futures are null
// then Composite never completes.
// On a practical level, I suspect that it has not happened often. ;)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I should make sure that it works correctly without hitting this. I think it does, but it may not have test coverage.

if (
((futures as System.Collections.IList)?.Count == 0) ||
((futures as Array)?.Length == 0)
Expand Down Expand Up @@ -661,6 +669,10 @@ public void RegisterHandlers (Action onComplete, Action onDispose) {
}

// FIXME: Set state to indeterminate once instead of twice
// REVIEW: I think that it would be possible to set state to indeterminate only once
// by splitting RegisterHandler_Impl in two:
// * a Disposable struct that would call TrySetIndeterminate on instantiation, and ClearIndeterminate on Dispose
// * a function that does the work of registering or running the handler
if (!RegisterHandler_Impl(onComplete, HandlerType.Completed))
RegisterHandler_Impl(onDispose, HandlerType.Disposed);
}
Expand Down