{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/16896","repository_url":"https://api.github.com/repos/dotnet/roslyn","labels_url":"https://api.github.com/repos/dotnet/roslyn/issues/16896/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/roslyn/issues/16896/comments","events_url":"https://api.github.com/repos/dotnet/roslyn/issues/16896/events","html_url":"https://github.com/dotnet/roslyn/issues/16896","id":204788061,"number":16896,"title":"Support the instrumented clause, for standard and simple code instrumentation","user":{"login":"msfcolombo","id":8314203,"avatar_url":"https://avatars.githubusercontent.com/u/8314203?v=3","gravatar_id":"","url":"https://api.github.com/users/msfcolombo","html_url":"https://github.com/msfcolombo","followers_url":"https://api.github.com/users/msfcolombo/followers","following_url":"https://api.github.com/users/msfcolombo/following{/other_user}","gists_url":"https://api.github.com/users/msfcolombo/gists{/gist_id}","starred_url":"https://api.github.com/users/msfcolombo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/msfcolombo/subscriptions","organizations_url":"https://api.github.com/users/msfcolombo/orgs","repos_url":"https://api.github.com/users/msfcolombo/repos","events_url":"https://api.github.com/users/msfcolombo/events{/privacy}","received_events_url":"https://api.github.com/users/msfcolombo/received_events","type":"User","site_admin":false},"labels":[{"id":169838071,"url":"https://api.github.com/repos/dotnet/roslyn/labels/Area-Language%20Design","name":"Area-Language Design","color":"5319e7","default":false},{"id":166676132,"url":"https://api.github.com/repos/dotnet/roslyn/labels/Resolution-External","name":"Resolution-External","color":"cccccc","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":3,"created_at":"2017-02-02T05:49:39Z","updated_at":"2017-02-09T18:08:02Z","closed_at":null,"body":"# Background\r\n\r\nIt's common in the implementation of logging, retry handlers and other helpers, to have a method that observes and manages execution of a code block. This is usually performed with lambdas:\r\n```\r\nInstrument(\"MyAction\", () =>\r\n{\r\n    // action code\r\n} );\r\n```\r\nLambdas imply the following:\r\n\r\n- Lambdas cannot interact with parent control flow, such as breaking or continuing a `for` statement, or immediately return a value for the declaring method. They force the developer to return a flag or temporary object to the observer method, which is then returned to the caller, which then must be evaluated and processed. That's a lot of boilerplate just to add instrumentation.\r\n- The observer interface and implementation typically require multiple versions: one for sync and other for async code, one for void and other for returned values. Providers can forget to implement one version.\r\n- The stacktrace contains two additional entries: the Observe call and an internally generated method for the lambda.\r\n- Lambdas often require an additional heap object to provide access to variables outside the lambda body.\r\n\r\nSome people suggest the `using` keyword as way of avoiding lambda issues, but `using` doesn't notify the `IDisposable` object about exceptions. This prevents observation of certain outcomes. Also, the `IDisposable` interface is designed for release of unmanaged resources. There is a general assumption that an object cannot be reused after it's disposed, and this assumption doesn't hold for observers. And finally, `using` cannot transparently issue a retry attempt.\r\n\r\n# Proposal\r\n\r\nThe proposal is to make C# support a clear form of instrumentation:\r\n```\r\ninstrumented (object-Expr)\r\n{\r\n    // action code\r\n}\r\n```\r\nThe object-Expr must return an instance of `IInstrument`. The action code is free to return a value for the declaring method, or to interact with parent flow statements (ie, it can have `break` and `continue`). This makes instrumentation much cleaner and less invasive and disruptive than a lambda. There are clear benefits in readability and editability, not to mention simpler stacktraces and mildly improved performance.\r\n\r\nThe above code is compiled roughly as if the developer had written:\r\n```\r\nvar _temp = object-Expr;\r\nfor (var _i = 0; ; ++_i) {\r\n    _temp.NotifyAttempt(_i);\r\n    try\r\n    {\r\n        // action code\r\n    }\r\n    catch (Exception ex)\r\n    {\r\n        if (_temp.NotifyException(_i, ex))\r\n            throw; // Instrumenter wants rethrow.\r\n        continue; // Instrumenter wants retry.\r\n    }\r\n    _temp.NotifySuccess(_i);\r\n    break;\r\n}\r\n```\r\nWith this feature, a provider of `IInstrument` implementation can:\r\n\r\n- Determine the time when the action started and finished, regardless of outcome.\r\n- Decide if an exception is abortive or if a retry should be attempted.\r\n- Retry as many times as wanted, knowing the number of each iteration.\r\n- Cache and reuse the object especified by object-Expr.\r\n- Throw an exception if the action took longer than expected, at the `NotifySuccess` call.\r\n\r\nThe provider can also back off (for instance, sleep) during execution of `NotifyAttempt`. In order to support async code, the `IInstrument` interface should also declare the async version `NotifyAttemptAsync`. The compiler decides which method to call from the style of parent method. This brings the additional benefits:\r\n\r\n- The provider of an observer cannot forget to support asynchronous code.\r\n- The user will have a single syntax for both sync and async code: `instrument (foo) { ... }`.\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/comments/278723836","html_url":"https://github.com/dotnet/roslyn/issues/16896#issuecomment-278723836","issue_url":"https://api.github.com/repos/dotnet/roslyn/issues/16896","id":278723836,"user":{"login":"msfcolombo","id":8314203,"avatar_url":"https://avatars.githubusercontent.com/u/8314203?v=3","gravatar_id":"","url":"https://api.github.com/users/msfcolombo","html_url":"https://github.com/msfcolombo","followers_url":"https://api.github.com/users/msfcolombo/followers","following_url":"https://api.github.com/users/msfcolombo/following{/other_user}","gists_url":"https://api.github.com/users/msfcolombo/gists{/gist_id}","starred_url":"https://api.github.com/users/msfcolombo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/msfcolombo/subscriptions","organizations_url":"https://api.github.com/users/msfcolombo/orgs","repos_url":"https://api.github.com/users/msfcolombo/repos","events_url":"https://api.github.com/users/msfcolombo/events{/privacy}","received_events_url":"https://api.github.com/users/msfcolombo/received_events","type":"User","site_admin":false},"created_at":"2017-02-09T18:08:02Z","updated_at":"2017-02-09T18:08:02Z","body":"@YaakovDavis What about:\r\n```\r\nwith (benchmarker, reattmeptor, ...)\r\n{\r\n    ...\r\n}\r\n```\r\n?"}}
{"actor":{"display_login":"msfcolombo"}}