none
How to Choose Schedulers in Rx 2.0 RRS feed

  • Question

  • Hi Bart,

    Thinking about your answer to my previous question a bit more, would it make sense to create an abstraction in Rx (vNext) and perhaps deprecate the platform-specific scheduler types?

    For example:

    Scheduler.Synchronous would internally use Scheduler.Immediate.

    Scheduler.Asynchronous would internally use Scheduler.CurrentThread.  (Edit: Or perhaps DispatcherScheduler)

    Scheduler.Concurrent would internally use the best platform-specific scheduler for the job.

    Rx would choose the most appropriate scheduler for concurrency internally based on the latest performance-enhancing heuristics, freeing users from having to arbitrarily choose between platform-specific schedulers that are (often?) swapped out internally for different implementations anyway, based on the aforementioned performance enhancements.

    If this is too generalized, then what criteria should we consider when choosing a scheduler now?  It seems that the following older discussion is no longer relevant as of Rx 2.0:

    http://social.msdn.microsoft.com/Forums/en/rx/thread/146833b0-fc4e-4f9b-adef-472aa2bb30b3

    Thanks,
    Dave


    http://davesexton.com/blog

    • Edited by Dave Sexton Saturday, August 25, 2012 1:30 PM Included DispatcherScheduler
    Saturday, August 25, 2012 4:48 AM

All replies

  • Interesting discussion.

    I find that most of the time, choosing the proper scheduler is a very complex task, and does not solely rely on the three semantic types of operations you've mentioned. Choosing a scheduler is most of the time a matter of greater logical operations, and not just a specific operator.

    I've architected most of my projects around the use of abstracted schedulers using IScheduler, and banned (via FxCop) the use of the Scheduler class. Appropriate schedulers are being injected into concrete classes by someone that knows exactly what he is doing and what the Rx (or custom) Schedulers do, where and when.

    A very good example of wrong choosing of schedulers is the use of Scheduler.Immediate in Observable.Return, which has the very unfortunate effect of pushing its value during the subscription phase. This can be devastating if a complex query is subscribed from the UI Thread.

    Also, I find the Scheduler class to be a problem for testing, because it is using the Singleton pattern based on static variables. It cannot be swapped out nor decorated easily in the current implementation of Rx.

    Thanks,
    Jerome

    Sunday, August 26, 2012 7:41 PM
  • Hi Jerome,

    Thanks for your input.  I agree that the static nature of the Scheduler class may not be abstract enough when designing robust applications, and certainly using IScheduler as an abstraction is sometimes more appropriate, but I don't see how your points refute my original suggestion.

    > choosing the proper scheduler is a very complex task, and does not solely rely on the three semantic types of operations

    Are you aware of the performance improvements in Rx 2.0?  Did you read the discussion to which I had linked?

    The point was that even if you think you're using ThreadPoolScheduler, you might not be.  This behavior may be more common than expected, perhaps for the other kinds of platform-specific schedulers as well; i.e., TaskPoolScheduler comes to mind.

    The following list contains all of the public schedulers in Rx 2.0.  I've marked the platform-specific schedulers in bold.

    • ControlScheduler (System.Reactive.Windows.Forms)
    • CurrentThreadScheduler 
    • DefaultScheduler (System.Reactive.Core)
    • DispatcherScheduler (System.Reactive.Windows.Threading)
    • EventLoopScheduler (System.Reactive.PlatformServices)
    • HistoricalScheduler 
    • ImmediateScheduler 
    • NewThreadScheduler (System.Reactive.PlatformServices)
    • SynchronizationContextScheduler 
    • TaskPoolScheduler (System.Reactive.PlatformServices)
    • ThreadPoolScheduler (System.Reactive.PlatformServices)
    • VirtualTimeScheduler 
    • TestScheduler

    I'm wondering if the list can be simplified, though I realize that certain schedulers should exist publicly due to their use of constructor parameters; e.g., TaskPoolScheduler

    Scheduler.Synchronous would internally use Scheduler.Immediate.

    If the Rx team's recommendation, based on Bart's response to our previous discussion (linked above), is that whether or not a scheduler introduces concurrency (or asynchrony in general) is more important than its exact behavior, then perhaps "synchronous" is a better name than "immediate".

    Scheduler.Asynchronous would internally use DispatcherScheduler or ControlScheduler, falling back to SynchronizationContextScheduler and finally CurrentThreadScheduler.

    The goal above is to introduce asynchrony, but not necessarily concurrency.

    Scheduler.Concurrent would internally use the best platform-specific scheduler for the job based on the service model being used for platform enlightenment, which I guess essentially means that any operator requesting ISchedulerLongRunning will get NewThreadScheduler, requests for ISchedulerPeriodic will get TaskPoolScheduler, and finally regular requests (DefaultScheduler perhaps?) will get ThreadPoolScheduler.

    That seems to cover all of the platform-specific schedulers (with the exception of EventLoopScheduler).  I don't know if this model is an oversimplification of the existing optimized behavior in Rx 2.0, so that's why I submitted this question to the forum.

    - Dave


    http://davesexton.com/blog

    • Edited by Dave Sexton Sunday, August 26, 2012 11:11 PM Simplified the use of ISchedulerPeriodic
    Sunday, August 26, 2012 11:02 PM
  • Thanks for the clarification.

    Yes, I am aware of the changes made to Rx 2.0, and I previously read the original post. It definitely is an interesting topic.

    I'm not refuting your suggestion, as it goes with the direction that Rx took from the beginning and has to live with, being the omnipresence of the Scheduler class. It is an interesting thing to know the direction that the Rx team somehow took internally with the DefaultSchedulers class. This class has semantic schedulers, based on the type of operation being performed (the "What"), and not the technicality of the "Where" and "When" the task should be scheduled.

    However, your suggestion is about moving the public Rx API from the "where" to run tasks to the "what" are the tasks, and if that is in line with the current implementation of Rx, it will be a hard-coded behavior. I'd say that it's fine if the behavior fits for most of the users of Rx.

    Yet, I find that this hard-coded behavior is complex to deal with, because for instance, you discover very quickly that one may kill the process if exceptions are not handled properly in the top-level observer of a query (note that SubscribeSafe does deal with this). It is then needed to have a custom scheduler that handles these exceptions properly, and you need to replace the use of Rx hard-coded behavior to that custom "safe" one for all operators that implicitly use Rx Scheduler class (Timer, Timeout, Delay, etc...).

    Again, I'm not refuting your suggestion. I do think it goes in the right direction regarding the abstraction of the "where" the tasks to be run, I just think it tying the implementation to one behavior is very limiting.

    - Jerome

    Monday, August 27, 2012 2:48 AM
  • Hi Jerome,

    I think it would be a tradeoff between simplification and flexibility.  To use my recommendation is much simpler for end users because it frees them from having to be aware of internal implementation details, which could change in the future.

    More specifically, the existing approach offers more flexibility by allowing you to choose among the various platform-specific schedulers, though you'll need to be aware of each operators' precedence for internal performance enhancements, the consequences of those enhancements affecting which scheduler may actually be used, and the differences in behavior when choosing between schedulers.  It seems to me that the Rx team is going to be much better at making these decisions for us in most cases, based on an operator's implementation and a hint from us about whether we'd prefer concurrency, asynchrony or synchrony.

    Frankly, I'm not sure this is an accurate generalization anyway - it seems that there's only currently 1 optimization that may swap schedulers internally: ThreadPool -> NewThread for long-running tasks; however, the Rx team is free to add more optimizations as they see fit, and since these kinds of optimizations are internal implementation details, we shouldn't need to be aware of them when choosing between schedulers.

    Note that I'm not suggesting to do away with the IScheduler interface or the ability to write custom schedulers.  I'm merely talking about hiding the platform-specific schedulers behind an optimization façade.

    > you discover very quickly that one may kill the process if exceptions are not handled properly [snip]
    > It is then needed to have a custom scheduler that handles these exceptions properly

    I don't agree with this.  Though I think exception handling behavior of schedulers is orthogonal to this discussion anyway.  All of the schedulers must behave similarly or else you may be surprised to find out that the performance optimizations have actually changed the exception handling behavior of your chosen scheduler, which will only be revealed at runtime.  With regards to changing the semantics when choosing schedulers, as I'm suggesting, exception handling behavior should be unaffected.  For custom schedulers, you can do whatever you want, though you should probably match the exception handling behavior of the built-in schedulers.

    > However, your suggestion is about moving the public Rx API from the "where" to run tasks to the "what" are the tasks

    Well, that's actually not my suggestion - I'm simply inferring it from Bart's reply in our previous discussion.  Bart wrote:

    "What matters more than the specific type of scheduler used is the achieved effect. In this particular case, one expects asynchrony caused by introduction of additional concurrency."

    My suggestion is to make Bart's explanation the actual semantics for choosing a scheduler.  The title of this post is apt, but here's another way of asking the same question: How am I supposed to choose between the schedulers that are offered if Rx's public contract for consuming schedulers reduces them all to merely concurrent, asynchronous or synchronous behaviors?

    Furthermore, what guidance is offered for choosing between the various platform-specific schedulers as they currently stand?  Is it worth my time to think about whether I should be using DispatcherScheduler or ControlScheduler or SynchronizationContextScheduler or CurrentThreadScheduler when all I really care about is introducing asynchrony?  Obviously, the infrastructure has enough information to do the right thing in most cases.  What about choosing between ThreadPoolScheduler or TaskPoolScheduler or NewThreadScheduler, when all I really care about is introducing concurrency?

    Is performance a factor that I must consider in general or is the recommendation to simply rely on Rx's internal optimizations in most scenarios?  In the cases where performance is a legitimate concern, will I be able to choose a more appropriate scheduler than the Rx infrastructure could?  Does this mean that I must know about the internal implementation details of every operator and explicitly call DisableOptimizations myself, when necessary, after determining that the default optimizations do not meet my needs?

    It seems that the infrastructure has enough information to make better decisions than me.  Consider the time optimizations in Rx 2.0 as an analogy: the infrastructure does a lot of work to ensure that time slippage is reduced or even eliminated in periodic actions, due to the introduction of a centralized process that handles all time-based operations.  Isn't that kind of what the task pool does for concurrency?  Perhaps it would be best if Rx choses an appropriate scheduler for me based on some hints; e.g., Scheduler.Concurrent.

    The Rx team could even go as far as changing schedulers in the middle of operations.  For example, what currently starts out as a long-running operation due to a particular operator's semantics and the current default behavior in Rx 2.0, may be better suited as a pooled operation to begin with, which can be auto-upgraded based on internal heuristics.

    - Dave


    http://davesexton.com/blog

    Monday, August 27, 2012 9:00 AM
  • Hi Dave,

    Indeed, the framework can do a fairly good job at choosing the specific Scheduler to run a task, there's no question about that.

    But as I said, the point I raised originally is not about technical improvements of the existing schedulers, or the addition of semantic schedulers, but rather a non-modifiable behavior for these new semantics.

    I'd have loved to have access to the SchedulerDefaults class to be able to choose what a ConstantTimeOperations, a TailRecursion or an AsyncConversion scheduler does.

    To illustrate a bit more my point, a simple example is Windows Phone applications. Windows Phone 7 devices only have a single CPU, which makes CPU intensive multithreading very expensive, introducing a lot of overhead cause my the lack of memory locality and memory bus bandwidth overload due to cache misses. In these applications, a single prioritized event loop scheduler does a very good job for all kinds of scheduled operations. Concurrency is out of the question at that point, considering that there is only one CPU.

    To be able to achieve that, I had to use the operators overrides that provide an IScheduler, because the default schedulers would always fall back to the ThreadPool. This adds unnecessary noise to the code, and forces a choice of scheduler, where I could have changed the schedulers globally, once.

    Interesting discussion, by the way :)

    Cheers,

    - Jerome

    Tuesday, August 28, 2012 3:06 AM
  • Hi Jerome,

    <summary>

    I think your argument is orthogonal to my proposal:

    I'm proposing to add less semantic schedulers as facades, behind which Rx can choose the most appropriate concrete implementations based on the desired level of asynchrony and the actual runtime environment, through the existing service model and future performance heuristics, perhaps even deprecating the platform-specific schedulers that have only a parameterless constructor.  By extension, I'm also proposing that these new facades replace Rx operators' default schedulers, though that would be an internal implementation detail.

    Ultimately, the point is that Rx already does this to a certain extent, based on my observations.  I'm merely suggesting that the public API is simplified to reflect this internal behavior.

    Note that I'm not proposing any changes to Rx's scheduler abstraction or operator design; i.e., parameterized scheduling.

    It seems like you're proposing that Rx should use assignable static schedulers to allow users to control the default behavior of operators - and you're not just referring to my proposed schedulers, you're referring to the entire Scheduler class.  Furthermore, you're suggesting that my proposal is flawed because the default behavior of my proposed facades can't be overridden globally.

    In other words, you're arguing against Rx's singleton design, whereas I'm proposing to include simpler, more dynamic scheduling.

    I believe your argument is orthogonal to my proposal.  It's the difference between:

    public static IScheduler Concurrent { get; }

    and

    public static IScheduler Concurrent { get; set; }

    </summary>

    > non-modifiable behavior for these new semantics [snip]

    I don't think this is a problem.  The whole question is whether Rx can choose better than us most of the time.  Whenever it can't, you can always implement your own scheduler and take advantage of parameterized operators.

    Currently, you can either use the default behavior provided by Rx operators or you can explicitly pass in a custom scheduler to any of the concurrency-capable operators.  My proposal simply offers better default behaviors and additional singleton schedulers that can be passed to operators whenever the default behavior isn't suitable, yet it's still desirable to have Rx make the choice as to how concurrency, asynchrony or synchrony is implemented.

    > access to the SchedulerDefaults class

    As far as I can tell, there is no SchedulerDefaults class defined in Rx, neither public nor private.  To be clear, are you suggesting that there should be?  Or are you referring to the Scheduler class?  (I'm assuming the latter.)

    > to be able to choose what a ConstantTimeOperations, a TailRecursion or an AsyncConversion scheduler does.

    Assignable static fields are very dangerous in library code because various application layers and third-party libraries can attempt to set them to different values, causing unexpected behavior.  Therefore, I highly doubt that the Rx team will move to this pattern in future releases.  As a matter of fact, the Rx team has already moved away from this pattern in older versions.  Parameterization is better.

    > To illustrate a bit more my point, a simple example is Windows Phone applications [snip]

    If you discover that the current default behavior doesn't meet your requirements, even in a WP7 application, then you'd still be free to pass in one of my proposed schedulers or custom schedulers, as needed.

    > This adds unnecessary noise to the code, and forces a choice of scheduler, where I could have changed the schedulers globally, once.

    See my point above about the danger of assignable static fields.

    - Dave


    http://davesexton.com/blog

    Tuesday, August 28, 2012 10:39 AM
  • Hi Jerome,

    Just want to tack something else onto my response:

    > To illustrate a bit more my point, a simple example is Windows Phone applications [snip]
    > This adds unnecessary noise to the code, and forces a choice of scheduler, where I could have changed the schedulers globally, once.

    My original question was whether Rx can choose better than us.  If the answer is yes, it would solve your problem as well.  No need to modify schedulers globally because Rx would recognize that it's running on a phone device and would then use the most appropriate default schedulers.

    Frankly, I don't know that Rx doesn't do this already.  Based on the Rx team blog, it seems like a safe bet to say they actually are doing this already, to a certain extent.  At least it appears to be true of periodic scheduling.

    Furthermore, I don't necessarily agree with your point that "concurrency" has to mean something different on a system that only has a single CPU.  I believe that if the system offers a threading model, then Rx should definitely take advantage of it by default when an operator needs to introduce concurrency.  The OS is ultimately responsible for scheduling, making things seem concurrent, as fairly as possible.

    - Dave


    http://davesexton.com/blog

    Tuesday, August 28, 2012 10:55 AM
  • Interesting discussion.

    Dave, I would really love to see that model that you posted in the 3rd post of this thread become the reality. Having just 4 modes (Synchronous, Asynchronous, Concurrent & Long running concurrent) to consider I think would make it far more accessible for new adopters and also easier to rationalize for teams adopting multiple platforms.

    A good example is the difference between Immediate and Current. This even trips up long-time users of Rx. Another example is the legitimate usage of ControlScheduler, DispatcherScheduler (and perhaps SynchronizationContextScheduler) for UI applications. I can imagine this creating needless meetings for Dev teams to decide on "Standards" for which style to adopt on their Client applications. Finally, and selfishly, If I start writing apps for WP7/8 or WinRT/Metro, it would be much nicer for me if my existing understanding of schedulers held true. I would rather continue using an "Asynchronous" and a "Concurrent" scheduler on all platforms, instead of deciding for each platform if NewThread/TaskPool/ThreadPool is the right choice.

    I am a big +1 for reducing the scheduler set. To summarize what Dave has previously suggested:

    Scheduler.Synchronous 
    ImmediateScheduler 
    Scheduler.Asynchronous
    CurrentThreadScheduler 
    ControlScheduler 
    DispatcherScheduler 
    SynchronizationContextScheduler ?
    Scheduler.Concurrent
    TaskPoolScheduler 
    ThreadPoolScheduler 
    Scheduler.ConcurrentLongRunning (potentially implementing the marker interface ISchedulerLongRunning)
    TaskPoolScheduler (but flagged as a LongRunning Task)
    NewThreadScheduler 
    + EventLoopScheduler (specialized case, usually for dedicated queues of serialized work)

    While keeping the VirtualTimeSchedulers for testing etc.

    Could this be built independently of Rx, e.g. could this become part of Rxx?

    Lee


    Lee Campbell http://LeeCampbell.blogspot.com

    Wednesday, August 29, 2012 9:56 AM
  • Hi Lee,

    Thanks for your input.  It's definitely something I'd consider adding to Rxx, though I think it would be best natively in Rx given that the performance heuristics could be somewhat complicated.  I'm wondering whether the Rx team has considered applying deeper performance heuristics in the future, even possibly changing the scheduler implementations mid-stream, as I've mentioned above.  It may also be worthwhile to consider "operator scheduler coalescing" as a performance enhancement.

    I'd like to update some things about your summary of my proposal.  First I'd like to be clear about precedence.  Second, I believe SynchronizationContextScheduler is necessary as a fallback before CurrentThreadScheduler; e.g., ASP.NET.  Finally, I'm not sure that having a separate long-running scheduler makes sense.  Operators are currently responsible for hinting long-running behavior through the scheduler service model by requesting the ISchedulerLongRunning interface, as you're aware.  Users shouldn't have to provide that hint, they'd just need to indicate the level of asynchrony that is desired and the façade would expose an appropriate implementation for ISchedulerLongRunning.  Do you agree?

    Proposal for the Public Schedulers of Rx vNext

    (Note: The schedulers suggested below were chosen based on implementations for .NET 4.5 only; however, the suggested abstractions would allow Rx to internally choose the best scheduler for the job considering the current platform.)

    • Scheduler.Synchronous = ImmediateScheduler
    • Scheduler.Asynchronous
      Standard = DispatcherScheduler / ControlScheduler -> SynchronizationContextScheduler -> CurrentThreadScheduler
      ISchedulerLongRunning = NewThreadScheduler
      ISchedulerPeriodic = DispatcherScheduler / ControlScheduler -> ThreadPoolScheduler
    • Scheduler.Concurrent
      Standard = ThreadPoolScheduler
      ISchedulerLongRunning = NewThreadScheduler
      ISchedulerPeriodic = ThreadPoolScheduler
    • ControlScheduler: Useful for when thread-affinity is required; e.g., Scheduler.Asynchronous may choose NewThreadScheduler, so users must be able to call .ObserveOn(ControlScheduler) explicitly at the end of a query if it must notify observers on the UI thread.
    • CurrentThreadScheduler: Useful for simple applications and experimentation.
    • DispatcherScheduler: Useful for when thread-affinity is required; e.g., Scheduler.Asynchronous may choose NewThreadScheduler, so users must be able to call .ObserveOnDispatcher() explicitly at the end of a query if it must notify observers on the UI thread.
    • EventLoopScheduler: Useful for dedicated work queues.  Its use implies that a new reference must be passed to more than one operator, thus making it difficult or impossible to hide behind scheduler façades.
    • HistoricalScheduler: Useful for instrumentation and diagnostics.
    • VirtualTimeScheduler: Useful for testing and experimentation.
    • TestScheduler: Useful for testing and experimentation.

    Any additional thoughts?  Changes?

    - Dave


    http://davesexton.com/blog

    Wednesday, August 29, 2012 11:26 AM
  • Hi Dave,

    My apologies for the delayed response.

    > It seems like you're proposing that Rx should use assignable static schedulers to allow
    > users to control the default behavior of operators - and you're not just referring to my proposed
    > schedulers, you're referring to the entire Scheduler class. 

    I'm not suggesting to use static schedulers fields at all, and as I said previously, I find the static-field singleton pattern problematic at so many levels, with testability, abstraction, flexibility, ... and I'm talking about the whole Scheduler class, which is currently (as of Rx 2.0) based on read-only static fields, configured internally by the System.Reactive.PlatformServices assembly with some custom dependency injection mechanism.

    The IPlaformEnlightmentProvider interface indirectly provides a unified scheduling strategy, based on the current platform, which I think is going in the right direction. But at this point, this infrastructure is not meant to be used publicly.

    > As far as I can tell, there is no SchedulerDefaults class defined in Rx, neither public nor private.
    > To be clear, are you suggesting that there should be?  Or are you referring to the
    > Scheduler class?  (I'm assuming the latter.)

    This class is internal, and is located in the System.Reactive.Core assembly. It is used by every operator that offers a default scheduler. I'm not suggesting this class should be public, but I do like the semantic granularity it is providing.

    > Assignable static fields are very dangerous in library code because various application layers
    > and third-party libraries can attempt to set them to different values, causing unexpected behavior. 

    That is actually not an excuse to exclude configurability just because a in-process library might behave badly, whether intentionally or not. There's a myriad of ways a third-party library can alter pretty badly the behavior of Application Domain wide configuration, and an Rx configuration is no different that these other settings.

    That said, I think that providing a freely accessible setter on the Scheduler class is not acceptable. Injecting a global platform scheduling strategy is probably a better way to do this.

    There are other ways to alter this static behavior without providing a setter (via the TLS), allowing for fine grained contextual scheduler configuration, but it may not be technically that relevant to the current discussion.

    > Therefore, I highly doubt that the Rx team will move to this pattern in future releases.  As a matter of
    > fact, the Rx team has already moved away from this pattern in older versions.  Parameterization is better.

    Actually, I understand that the change was driven by the lack of interface-based abstraction, rather that the use of static fields to hold default schedulers.

    Again, I'm not against your suggestion; it follows the direction that Rx is taking on the public API. I'm just adding to the conversation (and I'm directing it to the Rx team) that the Scheduler class should be more configurable.

    - Jerome

    Wednesday, September 5, 2012 2:09 AM
  • Hi Jerome,

    > I'm not suggesting to use static schedulers fields at all [snip]

    I understand, though my point wasn't really about static fields.  The nuts and bolts of how globals are reassigned to different behaviors is irrelevant.  Though I certainly respect that you'd want a more flexible solution than an assignable static field, the negative consequences remain the same: behavioral conflicts across multiple libraries.  Dependent libraries and applications may choose their own reasonable default scheduling mechanisms, thus conflicting with each other.  Parameterization is better.  It's more flexible, even though it's more verbose.

    > The IPlaformEnlightmentProvider interface indirectly provides a unified scheduling strategy [snip]

    I haven't looked at the internal workings of this myself, but I'd like to note that a unified scheduling strategy like the one that you're proposing can be useful, and it's safe as long as it can be controlled.  It can only be controlled if it remains private.  There will be no behavioral conflicts between third-party libraries because libraries can't change the internal behavior globally through this mechanism.

    Furthermore, a primary factor of my original question is the understanding that the Rx team may be able to choose appropriate schedulers better than us.  Therefore, if they were to publicly expose a provider-based injection mechanism for specifying global default scheduling behaviors across the entire Rx library, it would undermine the entire purpose of simplifying and automating the choosing of schedulers (my proposal).

    > This class is internal, and is located in the System.Reactive.Core assembly [snip]
    > I'm not suggesting this class should be public, but I do like the semantic granularity it is providing.

    I actually did a search in Reflector before I had posted my reply and I came up with nothing.  Perhaps I spelled it wrong or I only searched the .NET 4.5 assemblies and it's only defined for a different platform?

    Regardless, it's clear that we have different agendas based on your opinion of this internal class.  I've been taking the position of being opposed to the semantic granularity of the current scheduling system, let alone the finer-grained granularity of internal subsystems.  I'm trying to propose a more course-grained system for publicly choosing schedulers and moving the details to the internal subsystems based on runtime heuristics to make finer-grained decisions automatically.

    > That is actually not an excuse to exclude configurability just because a in-process library might behave badly [snip]

    A third-party library could change the default to a perfectly reasonable behavior for itself, and yet cause profound conflicts with other reasonable defaults on which other third-party libraries depend.  This makes it very difficult for an application to trust and integrate third-party libraries as dependencies.  It also means that third-party libraries may dictate to applications which defaults they must use for Rx scheduling.  That seems really dangerous to me.

    Although you might argue that a library making such changes is behaving "badly", consider that if an application changes the defaults for itself it could theoretically break third-party libraries on which it depends.  In other words, the danger goes both ways.  It's a perfectly good reason to categorize this as an anti-pattern in a functional world such as Rx.

    > There's a myriad of ways a third-party library can alter pretty badly the behavior of Application Domain wide configuration

    That doesn't justify your use of this pattern.  It only highlights the existing flaws in the FCL.

    To be clear, I'm not suggesting that the FCL is perfect; however, when the provider pattern is applied to cross-cutting concerns, such as logging, the result is more of an annoyance than a showstopper.  If some third-party library forces me to use Log4Net instead of .NET's Trace APIs, then so be it - I will if I must, though without a smile on my face.  But when service configurations or ASP.NET providers for security, profiles and session state are changed by third-party libraries, the consequences are far more serious.  Applications can easily become tied to the whims of a third-party library, and applications can easily break third-party libraries that depend on certain configurations.

    The purpose of most globally configurable provider-based systems is not for third-party library developers to assign behavior, and it's not merely to avoid having to rely on parameterization (e.g., passing an IScheduler argument to individual operators), but is instead intended for applications to workaround shortcomings in an API, to reset behavior for backward compatibility, or to provide a global service on which the entire application will depend (cross-cutting concerns); e.g., logging, ASP.NET session state, security services, HTTP proxy configuration, etc.

    The application developer makes this decision.  Third-party libraries depending upon a certain configuration for cross-cutting behavior is generally unreasonable and dangerous.

    I believe it's also unreasonable to allow any third-party library to define how the proposed Scheduler.Concurrent works across an entire application, regardless of whether it's by reassigning a static field or by using a configurable provider-based design.

    I don't want to have to read third-party documentation or to ask other developers to find out what defaults they've chosen for Rx.  I especially don't want to be forced to use custom default behaviors that are entirely inappropriate for my application.

    I want Rx to choose schedulers for me, whenever possible.  I also like having the flexibility to pass in IScheduler instances as arguments of individual operators to change the scheduling behavior to meet the needs of a given query.

    > Actually, I understand that the change was driven by the lack of interface-based abstraction,
    > rather that the use of static fields to hold default schedulers.

    I disagree.  In the video that I linked to, Wes mentions early on that the change was driven by the lack of composability due to the use of a "global static variable" as a "default synchronization context" (he's referring to Observable.Context).  Parameterizing operators and defining a new interface that can be passed to ObserveOn were the means to accomplish their primary goal of composability.

    The key issue, which I tried to relate to your argument, was that attempting to provide a global context though which all operators scheduled actions by default was dangerous and inflexible, yet being able to assign the context at runtime to different default scheduling behaviors was obviously an even worse solution to the original problems that they were trying to solve.  They found parameterization to be better for Rx.

    > Again, I'm not against your suggestion; it follows the direction that Rx is taking on the public API [snip]

    I appreciate that.  The purpose of posting was to gather feedback for my question, so if you're for or against it then that's great.

    But frankly, I still feel that your argument is orthogonal to my original question.  I tried to develop a proposal for a scheduler façade mechanism.  If you're proposing a provider-based design that can change the underlying behavior of that façade, it could be done.  But my original question still remains:

    "If this is too generalized, then what criteria should we consider when choosing a scheduler now?"

    My question is still unanswered.  Making the Scheduler class more configurable is irrelevant.  Don't you agree?

    - Dave


    http://davesexton.com/blog

    Wednesday, September 5, 2012 6:47 PM