001package arez; 002 003import arez.spy.ComputeCompleteEvent; 004import arez.spy.ComputeStartEvent; 005import arez.spy.ObserveCompleteEvent; 006import arez.spy.ObserveStartEvent; 007import arez.spy.ObserverCreateEvent; 008import arez.spy.ObserverDisposeEvent; 009import arez.spy.ObserverInfo; 010import grim.annotations.OmitSymbol; 011import java.util.Objects; 012import java.util.stream.Collectors; 013import javax.annotation.Nonnull; 014import javax.annotation.Nullable; 015import static org.realityforge.braincheck.Guards.*; 016 017/** 018 * A node within Arez that is notified of changes in 0 or more Observables. 019 */ 020public final class Observer 021 extends Node 022{ 023 /** 024 * The component that this observer is contained within. 025 * This should only be set if {@link Arez#areNativeComponentsEnabled()} is true but may also be null if 026 * the observer is a "top-level" observer. 027 */ 028 @OmitSymbol( unless = "arez.enable_native_components" ) 029 @Nullable 030 private final Component _component; 031 /** 032 * The reference to the ComputableValue if this observer is a derivation. 033 */ 034 @Nullable 035 private final ComputableValue<?> _computableValue; 036 /** 037 * The observables that this observer receives notifications from. 038 * These are the dependencies within the dependency graph and will 039 * typically correspond to the observables that were accessed in the 040 * last transaction that this observer was tracking. 041 * 042 * <p>This list should contain no duplicates.</p> 043 */ 044 @Nonnull 045 private FastList<ObservableValue<?>> _dependencies = new FastList<>(); 046 /** 047 * The list of hooks that this observer that will be invoked when it deactivates or 048 * has already been invoked as part of the register/activate process. 049 * These correspond to the hooks that were registered in the last 050 * transaction that this observer was tracking. 051 */ 052 @Nonnull 053 private HookMap _hooks = new HookMap(); 054 /** 055 * Observe function to invoke if any. 056 * This may be null if external executor is responsible for executing the observe function via 057 * methods such as {@link ArezContext#observe(Observer, Function, Object...)}. If this is null then 058 * {@link #_onDepsChange} must not be null. 059 */ 060 @Nullable 061 private final Procedure _observe; 062 /** 063 * Callback invoked when dependencies are updated. 064 * This may be null when the observer re-executes the observe function when dependencies change 065 * but in that case {@link #_observe} must not be null. 066 */ 067 @Nullable 068 private final Procedure _onDepsChange; 069 /** 070 * Cached info object associated with element. 071 * This should be null if {@link Arez#areSpiesEnabled()} is false. 072 */ 073 @OmitSymbol( unless = "arez.enable_spies" ) 074 @Nullable 075 private ObserverInfo _info; 076 /** 077 * A bitfield that contains config time and runtime flags/state. 078 * See the values in {@link Flags} that are covered by the masks 079 * {@link Flags#RUNTIME_FLAGS_MASK} and {@link Flags#CONFIG_FLAGS_MASK} 080 * for acceptable values. 081 */ 082 private int _flags; 083 @Nonnull 084 private final Task _task; 085 086 Observer( @Nonnull final ComputableValue<?> computableValue, final int flags ) 087 { 088 this( Arez.areZonesEnabled() ? computableValue.getContext() : null, 089 null, 090 Arez.areNamesEnabled() ? computableValue.getName() : null, 091 computableValue, 092 computableValue::compute, 093 null, 094 flags | 095 ( Flags.KEEPALIVE == Flags.getScheduleType( flags ) ? 0 : Flags.DEACTIVATE_ON_UNOBSERVE ) | 096 Task.Flags.runType( flags, Flags.KEEPALIVE == Flags.getScheduleType( flags ) ? 097 Task.Flags.RUN_NOW : 098 Task.Flags.RUN_LATER ) | 099 ( Arez.shouldEnforceTransactionType() ? Flags.READ_ONLY : 0 ) | 100 Flags.NESTED_ACTIONS_DISALLOWED | 101 Flags.dependencyType( flags ) ); 102 } 103 104 Observer( @Nullable final ArezContext context, 105 @Nullable final Component component, 106 @Nullable final String name, 107 @Nullable final Procedure observe, 108 @Nullable final Procedure onDepsChange, 109 final int flags ) 110 { 111 this( context, 112 component, 113 name, 114 null, 115 observe, 116 onDepsChange, 117 flags | 118 ( null == observe ? Flags.APPLICATION_EXECUTOR : Flags.KEEPALIVE ) | 119 Task.Flags.runType( flags, null == observe ? Task.Flags.RUN_LATER : Task.Flags.RUN_NOW ) | 120 Flags.nestedActionRule( flags ) | 121 Flags.dependencyType( flags ) | 122 Transaction.Flags.transactionMode( flags ) ); 123 } 124 125 private Observer( @Nullable final ArezContext context, 126 @Nullable final Component component, 127 @Nullable final String name, 128 @Nullable final ComputableValue<?> computableValue, 129 @Nullable final Procedure observe, 130 @Nullable final Procedure onDepsChange, 131 final int flags ) 132 { 133 super( context, name ); 134 _task = new Task( context, 135 name, 136 this::invokeReaction, 137 ( flags & Task.Flags.OBSERVER_TASK_FLAGS_MASK ) | 138 Task.Flags.NO_REGISTER_TASK | 139 Task.Flags.NO_WRAP_TASK ); 140 _flags = ( flags & ~Task.Flags.OBSERVER_TASK_FLAGS_MASK ) | Flags.STATE_INACTIVE; 141 if ( Arez.shouldCheckInvariants() ) 142 { 143 if ( Arez.shouldEnforceTransactionType() ) 144 { 145 invariant( () -> Transaction.Flags.isTransactionModeValid( flags ), 146 () -> "Arez-0079: Observer named '" + getName() + "' incorrectly specified both READ_ONLY " + 147 "and READ_WRITE transaction mode flags." ); 148 } 149 else 150 { 151 invariant( () -> !Transaction.Flags.isTransactionModeSpecified( flags ), 152 () -> "Arez-0082: Observer named '" + getName() + "' specified transaction mode '" + 153 Transaction.Flags.getTransactionModeName( flags ) + "' when " + 154 "Arez.enforceTransactionType() is false." ); 155 } 156 invariant( () -> Task.Flags.isPriorityValid( _task.getFlags() ), 157 () -> "Arez-0080: Observer named '" + getName() + "' has invalid priority " + 158 Task.Flags.getPriorityIndex( _task.getFlags() ) + "." ); 159 invariant( () -> Task.Flags.isRunTypeValid( _task.getFlags() ), 160 () -> "Arez-0081: Observer named '" + getName() + "' incorrectly specified both " + 161 "RUN_NOW and RUN_LATER flags." ); 162 invariant( () -> 0 == ( flags & Observer.Flags.RUN_NOW ) || null != observe, 163 () -> "Arez-0206: Observer named '" + getName() + "' incorrectly specified " + 164 "RUN_NOW flag but the observe function is null." ); 165 invariant( () -> Arez.areNativeComponentsEnabled() || null == component, 166 () -> "Arez-0083: Observer named '" + getName() + "' has component specified but " + 167 "Arez.areNativeComponentsEnabled() is false." ); 168 invariant( () -> Task.Flags.getPriority( flags ) != Task.Flags.PRIORITY_LOWEST || 169 0 == ( flags & Flags.OBSERVE_LOWER_PRIORITY_DEPENDENCIES ), 170 () -> "Arez-0184: Observer named '" + getName() + "' has LOWEST priority but has passed " + 171 "OBSERVE_LOWER_PRIORITY_DEPENDENCIES option which should not be present as the observer " + 172 "has no lower priority." ); 173 invariant( () -> null != observe || null != onDepsChange, 174 () -> "Arez-0204: Observer named '" + getName() + "' has not supplied a value for either the " + 175 "observe parameter or the onDepsChange parameter." ); 176 // Next lines are impossible situations to create from tests. Add asserts to verify this. 177 assert Flags.KEEPALIVE != Flags.getScheduleType( flags ) || null != observe; 178 assert Flags.APPLICATION_EXECUTOR != Flags.getScheduleType( flags ) || null == observe; 179 assert !( Observer.Flags.RUN_NOW == ( flags & Observer.Flags.RUN_NOW ) && 180 Flags.KEEPALIVE != Flags.getScheduleType( flags ) && 181 null != computableValue ); 182 invariant( () -> Flags.isNestedActionsModeValid( flags ), 183 () -> "Arez-0209: Observer named '" + getName() + "' incorrectly specified both the " + 184 "NESTED_ACTIONS_ALLOWED flag and the NESTED_ACTIONS_DISALLOWED flag." ); 185 invariant( () -> Flags.isScheduleTypeValid( flags ), 186 () -> "Arez-0210: Observer named '" + getName() + "' incorrectly specified multiple " + 187 "schedule type flags (KEEPALIVE, DEACTIVATE_ON_UNOBSERVE, APPLICATION_EXECUTOR)." ); 188 invariant( () -> ( ~( Flags.RUNTIME_FLAGS_MASK | Flags.CONFIG_FLAGS_MASK ) & flags ) == 0, 189 () -> "Arez-0207: Observer named '" + getName() + "' specified illegal flags: " + 190 ( ~( Flags.RUNTIME_FLAGS_MASK | Flags.CONFIG_FLAGS_MASK ) & flags ) ); 191 } 192 assert null == computableValue || !Arez.areNamesEnabled() || computableValue.getName().equals( name ); 193 _component = Arez.areNativeComponentsEnabled() ? component : null; 194 _computableValue = computableValue; 195 _observe = observe; 196 _onDepsChange = onDepsChange; 197 198 executeObserveNextIfPresent(); 199 200 if ( null == _computableValue ) 201 { 202 if ( null != _component ) 203 { 204 _component.addObserver( this ); 205 } 206 else if ( Arez.areRegistriesEnabled() ) 207 { 208 getContext().registerObserver( this ); 209 } 210 } 211 if ( null == _computableValue ) 212 { 213 if ( willPropagateSpyEvents() ) 214 { 215 getSpy().reportSpyEvent( new ObserverCreateEvent( asInfo() ) ); 216 } 217 if ( null != _observe ) 218 { 219 initialSchedule(); 220 } 221 } 222 } 223 224 void initialSchedule() 225 { 226 getContext().scheduleReaction( this ); 227 _task.triggerSchedulerInitiallyUnlessRunLater(); 228 } 229 230 boolean areArezDependenciesRequired() 231 { 232 assert Arez.shouldCheckApiInvariants(); 233 return Flags.AREZ_DEPENDENCIES == ( _flags & Flags.AREZ_DEPENDENCIES ); 234 } 235 236 boolean areExternalDependenciesAllowed() 237 { 238 assert Arez.shouldCheckApiInvariants(); 239 return Flags.AREZ_OR_EXTERNAL_DEPENDENCIES == ( _flags & Flags.AREZ_OR_EXTERNAL_DEPENDENCIES ); 240 } 241 242 /** 243 * Return true if the Observer supports invocations of {@link #schedule()} from non-arez code. 244 * This is true if both a {@link #_observe} and {@link #_onDepsChange} parameters 245 * are provided at construction. 246 */ 247 boolean supportsManualSchedule() 248 { 249 assert Arez.shouldCheckApiInvariants(); 250 return null != _observe && null != _onDepsChange; 251 } 252 253 boolean isApplicationExecutor() 254 { 255 assert Arez.shouldCheckApiInvariants(); 256 return null == _observe; 257 } 258 259 boolean nestedActionsAllowed() 260 { 261 assert Arez.shouldCheckApiInvariants(); 262 return 0 != ( _flags & Flags.NESTED_ACTIONS_ALLOWED ); 263 } 264 265 boolean canObserveLowerPriorityDependencies() 266 { 267 assert Arez.shouldCheckApiInvariants(); 268 return 0 != ( _flags & Flags.OBSERVE_LOWER_PRIORITY_DEPENDENCIES ); 269 } 270 271 boolean noReportResults() 272 { 273 assert Arez.areSpiesEnabled(); 274 return 0 != ( _flags & Observer.Flags.NO_REPORT_RESULT ); 275 } 276 277 boolean isComputableValue() 278 { 279 return null != _computableValue; 280 } 281 282 /** 283 * Make the Observer INACTIVE and release any resources associated with observer. 284 * The applications should NOT interact with the Observer after it has been disposed. 285 */ 286 @Override 287 public void dispose() 288 { 289 if ( isNotDisposedOrDisposing() ) 290 { 291 getContext().safeAction( Arez.areNamesEnabled() ? getName() + ".dispose" : null, 292 this::performDispose, 293 ActionFlags.NO_VERIFY_ACTION_REQUIRED ); 294 if ( !isComputableValue() ) 295 { 296 if ( willPropagateSpyEvents() ) 297 { 298 reportSpyEvent( new ObserverDisposeEvent( asInfo() ) ); 299 } 300 if ( null != _component ) 301 { 302 _component.removeObserver( this ); 303 } 304 else if ( Arez.areRegistriesEnabled() ) 305 { 306 getContext().deregisterObserver( this ); 307 } 308 } 309 if ( null != _computableValue ) 310 { 311 _computableValue.dispose(); 312 } 313 _task.dispose(); 314 markAsDisposed(); 315 } 316 } 317 318 private void performDispose() 319 { 320 getContext().getTransaction().reportDispose( this ); 321 markDependenciesLeastStaleObserverAsUpToDate(); 322 setState( Flags.STATE_DISPOSING ); 323 } 324 325 void markAsDisposed() 326 { 327 _flags = Flags.setState( _flags, Flags.STATE_DISPOSED ); 328 } 329 330 @Override 331 public boolean isDisposed() 332 { 333 return Flags.STATE_DISPOSED == getState(); 334 } 335 336 boolean isNotDisposedOrDisposing() 337 { 338 return Flags.STATE_DISPOSING < getState(); 339 } 340 341 /** 342 * Return true during invocation of dispose, false otherwise. 343 * 344 * @return true during invocation of dispose, false otherwise. 345 */ 346 boolean isDisposing() 347 { 348 return Flags.STATE_DISPOSING == getState(); 349 } 350 351 /** 352 * Return the state of the observer relative to the observers dependencies. 353 * 354 * @return the state of the observer relative to the observers dependencies. 355 */ 356 int getState() 357 { 358 return Flags.getState( _flags ); 359 } 360 361 int getLeastStaleObserverState() 362 { 363 return Flags.getLeastStaleObserverState( _flags ); 364 } 365 366 /** 367 * Return true if observer creates a READ_WRITE transaction. 368 * 369 * @return true if observer creates a READ_WRITE transaction. 370 */ 371 boolean isMutation() 372 { 373 assert Arez.shouldEnforceTransactionType(); 374 return 0 != ( _flags & Flags.READ_WRITE ); 375 } 376 377 /** 378 * Return true if the observer is active. 379 * Being "active" means that the state of the observer is not {@link Flags#STATE_INACTIVE}, 380 * {@link Flags#STATE_DISPOSING} or {@link Flags#STATE_DISPOSED}. 381 * 382 * <p>An inactive observer has no dependencies and depending on the type of observer may 383 * have other consequences. i.e. An inactive observer will never be scheduled even if it has a 384 * reaction.</p> 385 * 386 * @return true if the Observer is active. 387 */ 388 boolean isActive() 389 { 390 return Flags.isActive( _flags ); 391 } 392 393 /** 394 * Return true if the observer is not active. 395 * The inverse of {@link #isActive()} 396 * 397 * @return true if the Observer is inactive. 398 */ 399 boolean isInactive() 400 { 401 return !isActive(); 402 } 403 404 /** 405 * This method should be invoked if the observer has non-arez dependencies and one of 406 * these dependencies has been updated. This will mark the observer as stale and reschedule 407 * the reaction if necessary. The method must be invoked from within a read-write transaction. 408 * the reaction if necessary. The method must be invoked from within a read-write transaction. 409 */ 410 public void reportStale() 411 { 412 if ( Arez.shouldCheckApiInvariants() ) 413 { 414 apiInvariant( this::areExternalDependenciesAllowed, 415 () -> "Arez-0199: Observer.reportStale() invoked on observer named '" + getName() + 416 "' but the observer has not specified AREZ_OR_EXTERNAL_DEPENDENCIES flag." ); 417 apiInvariant( () -> getContext().isTransactionActive(), 418 () -> "Arez-0200: Observer.reportStale() invoked on observer named '" + getName() + 419 "' when there is no active transaction." ); 420 apiInvariant( () -> getContext().getTransaction().isMutation(), 421 () -> "Arez-0201: Observer.reportStale() invoked on observer named '" + getName() + 422 "' when the active transaction '" + getContext().getTransaction().getName() + 423 "' is READ_ONLY rather than READ_WRITE." ); 424 } 425 if ( Arez.shouldEnforceTransactionType() && Arez.shouldCheckInvariants() ) 426 { 427 getContext().getTransaction().markTransactionAsUsed(); 428 } 429 setState( Flags.STATE_STALE ); 430 } 431 432 /** 433 * Set the state of the observer. 434 * Call the hook actions for relevant state change. 435 * This is equivalent to passing true in <code>schedule</code> parameter to {@link #setState(int, boolean)} 436 * 437 * @param state the new state of the observer. 438 */ 439 void setState( final int state ) 440 { 441 setState( state, true ); 442 } 443 444 /** 445 * Set the state of the observer. 446 * Call the hook actions for relevant state change. 447 * 448 * @param state the new state of the observer. 449 * @param schedule true if a state transition can cause observer to reschedule, false otherwise. 450 */ 451 void setState( final int state, final boolean schedule ) 452 { 453 if ( Arez.shouldCheckInvariants() ) 454 { 455 invariant( () -> getContext().isTransactionActive(), 456 () -> "Arez-0086: Attempt to invoke setState on observer named '" + getName() + "' when there is " + 457 "no active transaction." ); 458 invariantState(); 459 } 460 final int originalState = getState(); 461 if ( state != originalState ) 462 { 463 _flags = Flags.setState( _flags, state ); 464 if ( Arez.shouldCheckInvariants() && Flags.STATE_DISPOSED == originalState ) 465 { 466 fail( () -> "Arez-0087: Attempted to activate disposed observer named '" + getName() + "'." ); 467 } 468 else if ( null == _computableValue && Flags.STATE_STALE == state ) 469 { 470 if ( schedule ) 471 { 472 scheduleReaction(); 473 } 474 } 475 else if ( null != _computableValue && 476 Flags.STATE_UP_TO_DATE == originalState && 477 ( Flags.STATE_STALE == state || Flags.STATE_POSSIBLY_STALE == state ) ) 478 { 479 _computableValue.getObservableValue().reportPossiblyChanged(); 480 if ( schedule ) 481 { 482 scheduleReaction(); 483 } 484 } 485 else if ( Flags.STATE_INACTIVE == state || 486 ( Flags.STATE_INACTIVE != originalState && Flags.STATE_DISPOSING == state ) ) 487 { 488 if ( isComputableValue() ) 489 { 490 getComputableValue().completeDeactivate(); 491 } 492 final HookMap hooks = getHooks(); 493 for ( int i = 0, size = hooks.size(); i < size; i++ ) 494 { 495 runHook( hooks.valueAt( i ).getOnDeactivate(), ObserverError.ON_DEACTIVATE_ERROR ); 496 } 497 hooks.clear(); 498 clearDependencies(); 499 } 500 if ( Arez.shouldCheckInvariants() ) 501 { 502 invariantState(); 503 } 504 } 505 } 506 507 /** 508 * Run the supplied hook if non null. 509 * 510 * @param hook the hook to run. 511 */ 512 void runHook( @Nullable final Procedure hook, @Nonnull final ObserverError error ) 513 { 514 if ( null != hook ) 515 { 516 try 517 { 518 hook.call(); 519 } 520 catch ( final Throwable t ) 521 { 522 getContext().reportObserverError( this, error, t ); 523 } 524 } 525 } 526 527 /** 528 * Remove all dependencies, removing this observer from all dependencies in the process. 529 */ 530 void clearDependencies() 531 { 532 getDependencies().forEach( dependency -> { 533 dependency.removeObserver( this ); 534 if ( !dependency.hasObservers() ) 535 { 536 dependency.setLeastStaleObserverState( Flags.STATE_UP_TO_DATE ); 537 } 538 } ); 539 getDependencies().clear(); 540 } 541 542 /** 543 * Return the task associated with the observer. 544 * The task is used during scheduling. 545 * 546 * @return the task associated with the observer. 547 */ 548 @Nonnull 549 Task getTask() 550 { 551 return _task; 552 } 553 554 /** 555 * Schedule this observer if it does not already have a reaction pending. 556 * The observer will not actually react if it is not already marked as STALE. 557 */ 558 public void schedule() 559 { 560 if ( Arez.shouldCheckApiInvariants() ) 561 { 562 apiInvariant( this::supportsManualSchedule, 563 () -> "Arez-0202: Observer.schedule() invoked on observer named '" + getName() + 564 "' but supportsManualSchedule() returns false." ); 565 } 566 if ( Arez.shouldEnforceTransactionType() && getContext().isTransactionActive() && Arez.shouldCheckInvariants() ) 567 { 568 getContext().getTransaction().markTransactionAsUsed(); 569 } 570 executeObserveNextIfPresent(); 571 scheduleReaction(); 572 getContext().triggerScheduler(); 573 } 574 575 /** 576 * Schedule this observer if it does not already have a reaction pending. 577 */ 578 void scheduleReaction() 579 { 580 if ( isNotDisposed() ) 581 { 582 if ( Arez.shouldCheckInvariants() ) 583 { 584 invariant( this::isActive, 585 () -> "Arez-0088: Observer named '" + getName() + "' is not active but an attempt has been made " + 586 "to schedule observer." ); 587 } 588 if ( !getTask().isQueued() ) 589 { 590 getContext().scheduleReaction( this ); 591 } 592 } 593 } 594 595 /** 596 * Run the reaction in a transaction with the name and mode defined 597 * by the observer. If the reaction throws an exception, the exception is reported 598 * to the context global ObserverErrorHandlers 599 */ 600 void invokeReaction() 601 { 602 if ( isNotDisposed() ) 603 { 604 final long startedAt; 605 if ( willPropagateSpyEvents() ) 606 { 607 startedAt = System.currentTimeMillis(); 608 if ( isComputableValue() ) 609 { 610 reportSpyEvent( new ComputeStartEvent( getComputableValue().asInfo() ) ); 611 } 612 else 613 { 614 reportSpyEvent( new ObserveStartEvent( asInfo() ) ); 615 } 616 } 617 else 618 { 619 startedAt = 0; 620 } 621 Throwable error = null; 622 try 623 { 624 // ComputableValues may have calculated their values and thus be up to date so no need to recalculate. 625 if ( Flags.STATE_UP_TO_DATE != getState() ) 626 { 627 if ( shouldExecuteObserveNext() ) 628 { 629 executeOnDepsChangeNextIfPresent(); 630 runObserveFunction(); 631 } 632 else 633 { 634 assert null != _onDepsChange; 635 _onDepsChange.call(); 636 } 637 } 638 else if ( shouldExecuteObserveNext() ) 639 { 640 /* 641 * The observer should invoke onDepsChange next if the following conditions hold. 642 * - a manual schedule() invocation 643 * - the observer is not stale, and 644 * - there is an onDepsChange hook present 645 * 646 * This block ensures this is the case. 647 */ 648 executeOnDepsChangeNextIfPresent(); 649 } 650 } 651 catch ( final Throwable t ) 652 { 653 error = t; 654 getContext().reportObserverError( this, ObserverError.REACTION_ERROR, t ); 655 } 656 // start == 0 implies that spy events were enabled as part of observer, and thus we can skip this 657 // chain of events 658 if ( willPropagateSpyEvents() && 0 != startedAt ) 659 { 660 final int duration = Math.max( 0, (int) ( System.currentTimeMillis() - startedAt ) ); 661 if ( isComputableValue() ) 662 { 663 final ComputableValue<?> computableValue = getComputableValue(); 664 reportSpyEvent( new ComputeCompleteEvent( computableValue.asInfo(), 665 noReportResults() ? null : computableValue.getValue(), 666 computableValue.getError(), 667 duration ) ); 668 } 669 else 670 { 671 reportSpyEvent( new ObserveCompleteEvent( asInfo(), error, duration ) ); 672 } 673 } 674 } 675 } 676 677 private void runObserveFunction() 678 throws Throwable 679 { 680 assert null != _observe; 681 final Procedure action; 682 if ( Arez.shouldCheckInvariants() && areArezDependenciesRequired() ) 683 { 684 action = () -> { 685 _observe.call(); 686 final Transaction current = Transaction.current(); 687 688 final FastList<ObservableValue<?>> observableValues = current.getObservableValues(); 689 invariant( () -> Objects.requireNonNull( current.getTracker() ).isDisposing() || 690 ( null != observableValues && !observableValues.isEmpty() ), 691 () -> "Arez-0172: Observer named '" + getName() + "' that does not use an external executor " + 692 "completed observe function but is not observing any properties. As a result the observer " + 693 "will never be rescheduled." ); 694 }; 695 } 696 else 697 { 698 action = _observe; 699 } 700 getContext().rawObserve( this, action, null ); 701 } 702 703 /** 704 * Utility to mark all dependencies least stale observer as up to date. 705 * Used when the Observer is determined to be up todate. 706 */ 707 void markDependenciesLeastStaleObserverAsUpToDate() 708 { 709 final FastList<ObservableValue<?>> dependencies = getDependencies(); 710 for ( int i = 0, end = dependencies.size(); i < end; ++i ) 711 { 712 final ObservableValue<?> dependency = dependencies.get( i ); 713 assert null != dependency; 714 dependency.setLeastStaleObserverState( Flags.STATE_UP_TO_DATE ); 715 } 716 } 717 718 /** 719 * Determine if any dependency of the Observer has actually changed. 720 * If the state is POSSIBLY_STALE then recalculate any ComputableValue dependencies. 721 * If any ComputableValue dependencies actually changed then the STALE state will 722 * be propagated. 723 * 724 * <p>By iterating over the dependencies in the same order that they were reported and 725 * stopping on the first change, all the recalculations are only called for ComputableValues 726 * that will be tracked by derivation. That is because we assume that if the first N 727 * dependencies of the derivation doesn't change then the derivation should run the same way 728 * up until accessing N-th dependency.</p> 729 * 730 * @return true if the Observer should be recomputed. 731 */ 732 boolean shouldCompute() 733 { 734 final int state = getState(); 735 switch ( state ) 736 { 737 case Flags.STATE_UP_TO_DATE: 738 return false; 739 case Flags.STATE_INACTIVE: 740 case Flags.STATE_STALE: 741 return true; 742 case Flags.STATE_POSSIBLY_STALE: 743 { 744 final FastList<ObservableValue<?>> dependencies = getDependencies(); 745 for ( int i = 0, end = dependencies.size(); i < end; ++i ) 746 { 747 final ObservableValue<?> observableValue = dependencies.get( i ); 748 assert null != observableValue; 749 if ( observableValue.isComputableValue() ) 750 { 751 final Observer owner = observableValue.getObserver(); 752 final ComputableValue<?> computableValue = owner.getComputableValue(); 753 try 754 { 755 computableValue.get(); 756 } 757 catch ( final Throwable ignored ) 758 { 759 } 760 // Call to get() will update this state if ComputableValue changed 761 if ( Flags.STATE_STALE == getState() ) 762 { 763 return true; 764 } 765 } 766 } 767 } 768 break; 769 default: 770 if ( Arez.shouldCheckInvariants() ) 771 { 772 fail( () -> "Arez-0205: Observer.shouldCompute() invoked on observer named '" + getName() + 773 "' but observer is in state " + Flags.getStateName( getState() ) ); 774 } 775 } 776 /* 777 * This results in POSSIBLY_STALE returning to UP_TO_DATE 778 */ 779 markDependenciesLeastStaleObserverAsUpToDate(); 780 return false; 781 } 782 783 /** 784 * Return the hooks. 785 * 786 * @return the hooks. 787 */ 788 @Nonnull 789 HookMap getHooks() 790 { 791 return _hooks; 792 } 793 794 /** 795 * Replace the current set of hooks with the supplied hooks. 796 * 797 * @param hooks the new set of hooks. 798 */ 799 void replaceHooks( @Nonnull final HookMap hooks ) 800 { 801 _hooks = Objects.requireNonNull( hooks ); 802 } 803 804 /** 805 * Return the dependencies. 806 * 807 * @return the dependencies. 808 */ 809 @Nonnull 810 FastList<ObservableValue<?>> getDependencies() 811 { 812 return _dependencies; 813 } 814 815 /** 816 * Replace the current set of dependencies with supplied dependencies. 817 * This should be the only mechanism via which the dependencies are updated. 818 * 819 * @param dependencies the new set of dependencies. 820 */ 821 void replaceDependencies( @Nonnull final FastList<ObservableValue<?>> dependencies ) 822 { 823 if ( Arez.shouldCheckInvariants() ) 824 { 825 invariantDependenciesUnique( "Pre replaceDependencies" ); 826 } 827 _dependencies = Objects.requireNonNull( dependencies ); 828 if ( Arez.shouldCheckInvariants() ) 829 { 830 invariantDependenciesUnique( "Post replaceDependencies" ); 831 invariantDependenciesBackLink( "Post replaceDependencies" ); 832 invariantDependenciesNotDisposed(); 833 } 834 } 835 836 /** 837 * Ensure the dependencies list contain no duplicates. 838 * Should be optimized away if invariant checking is disabled. 839 * 840 * @param context some useful debugging context used in invariant checks. 841 */ 842 void invariantDependenciesUnique( @Nonnull final String context ) 843 { 844 if ( Arez.shouldCheckExpensiveInvariants() ) 845 { 846 invariant( () -> getDependencies().size() == getDependencies().stream().collect( Collectors.toSet() ).size(), 847 () -> "Arez-0089: " + context + ": The set of dependencies in observer named '" + 848 getName() + "' is not unique. Current list: '" + 849 getDependencies().stream().map( Node::getName ).collect( Collectors.toList() ) + "'." ); 850 } 851 } 852 853 /** 854 * Ensure all dependencies contain this observer in the list of observers. 855 * Should be optimized away if invariant checking is disabled. 856 * 857 * @param context some useful debugging context used in invariant checks. 858 */ 859 void invariantDependenciesBackLink( @Nonnull final String context ) 860 { 861 if ( Arez.shouldCheckExpensiveInvariants() ) 862 { 863 getDependencies().forEach( observable -> 864 invariant( () -> observable.getObservers().contains( this ), 865 () -> "Arez-0090: " + context + ": Observer named '" + getName() + 866 "' has ObservableValue dependency named '" + observable.getName() + 867 "' which does not contain the observer in the list of " + 868 "observers." ) ); 869 invariantComputableValueObserverState(); 870 } 871 } 872 873 /** 874 * Ensure all dependencies are not disposed. 875 */ 876 void invariantDependenciesNotDisposed() 877 { 878 if ( Arez.shouldCheckExpensiveInvariants() ) 879 { 880 getDependencies().forEach( observable -> 881 invariant( observable::isNotDisposed, 882 () -> "Arez-0091: Observer named '" + getName() + "' has " + 883 "ObservableValue dependency named '" + observable.getName() + 884 "' which is disposed." ) ); 885 invariantComputableValueObserverState(); 886 } 887 } 888 889 /** 890 * Ensure that state field and other fields of the Observer are consistent. 891 */ 892 void invariantState() 893 { 894 if ( Arez.shouldCheckInvariants() ) 895 { 896 if ( isInactive() && !isDisposing() ) 897 { 898 invariant( () -> getDependencies().isEmpty(), 899 () -> "Arez-0092: Observer named '" + getName() + "' is inactive but still has dependencies: " + 900 getDependencies().stream().map( Node::getName ).collect( Collectors.toList() ) + "." ); 901 } 902 if ( null != _computableValue && _computableValue.isNotDisposed() ) 903 { 904 final ObservableValue<?> observable = _computableValue.getObservableValue(); 905 invariant( () -> Objects.equals( observable.isComputableValue() ? observable.getObserver() : null, this ), 906 () -> "Arez-0093: Observer named '" + getName() + "' is associated with an ObservableValue that " + 907 "does not link back to observer." ); 908 } 909 } 910 } 911 912 void invariantComputableValueObserverState() 913 { 914 if ( Arez.shouldCheckInvariants() ) 915 { 916 if ( isComputableValue() && isActive() && isNotDisposed() ) 917 { 918 invariant( () -> !getComputableValue().getObservableValue().getObservers().isEmpty() || 919 Objects.equals( getContext().getTransaction().getTracker(), this ), 920 () -> "Arez-0094: Observer named '" + getName() + "' is a ComputableValue and active but the " + 921 "associated ObservableValue has no observers." ); 922 } 923 } 924 } 925 926 /** 927 * Return the ComputableValue for Observer. 928 * This should not be called if observer is not part of a ComputableValue and will generate an invariant failure 929 * if invariants are enabled. 930 * 931 * @return the associated ComputableValue. 932 */ 933 @Nonnull 934 ComputableValue<?> getComputableValue() 935 { 936 if ( Arez.shouldCheckInvariants() ) 937 { 938 invariant( this::isComputableValue, 939 () -> "Arez-0095: Attempted to invoke getComputableValue on observer named '" + getName() + "' when " + 940 "is not a computable observer." ); 941 } 942 assert null != _computableValue; 943 return _computableValue; 944 } 945 946 @Nullable 947 Component getComponent() 948 { 949 return _component; 950 } 951 952 /** 953 * Return the info associated with this class. 954 * 955 * @return the info associated with this class. 956 */ 957 @SuppressWarnings( "ConstantConditions" ) 958 @OmitSymbol( unless = "arez.enable_spies" ) 959 @Nonnull 960 ObserverInfo asInfo() 961 { 962 if ( Arez.shouldCheckInvariants() ) 963 { 964 invariant( Arez::areSpiesEnabled, 965 () -> "Arez-0197: Observer.asInfo() invoked but Arez.areSpiesEnabled() returned false." ); 966 } 967 if ( Arez.areSpiesEnabled() && null == _info ) 968 { 969 _info = new ObserverInfoImpl( getContext().getSpy(), this ); 970 } 971 return Arez.areSpiesEnabled() ? _info : null; 972 } 973 974 @Nullable 975 Procedure getObserve() 976 { 977 return _observe; 978 } 979 980 @Nullable 981 Procedure getOnDepsChange() 982 { 983 return _onDepsChange; 984 } 985 986 boolean isKeepAlive() 987 { 988 return Flags.KEEPALIVE == Flags.getScheduleType( _flags ); 989 } 990 991 boolean shouldExecuteObserveNext() 992 { 993 return 0 != ( _flags & Flags.EXECUTE_OBSERVE_NEXT ); 994 } 995 996 void executeObserveNextIfPresent() 997 { 998 if ( null != _observe ) 999 { 1000 _flags |= Flags.EXECUTE_OBSERVE_NEXT; 1001 } 1002 } 1003 1004 private void executeOnDepsChangeNextIfPresent() 1005 { 1006 if ( null != _onDepsChange ) 1007 { 1008 _flags &= ~Flags.EXECUTE_OBSERVE_NEXT; 1009 } 1010 } 1011 1012 public static final class Flags 1013 { 1014 /** 1015 * The flag can be passed to actions or observers to force the action to not report result to spy infrastructure. 1016 */ 1017 public static final int NO_REPORT_RESULT = 1 << 12; 1018 /** 1019 * Highest priority. 1020 * This priority should be used when the observer will dispose or release other reactive elements 1021 * (and thus remove elements from being scheduled). 1022 * <p>Only one of the PRIORITY_* flags should be applied to observer.</p> 1023 * 1024 * @see arez.annotations.Priority#HIGHEST 1025 * @see arez.spy.Priority#HIGHEST 1026 * @see Task.Flags#PRIORITY_HIGHEST 1027 */ 1028 public static final int PRIORITY_HIGHEST = 0b001 << 15; 1029 /** 1030 * High priority. 1031 * To reduce the chance that downstream elements will react multiple times within a single 1032 * reaction round, this priority should be used when the observer may trigger many downstream 1033 * reactions. 1034 * <p>Only one of the PRIORITY_* flags should be applied to observer.</p> 1035 * 1036 * @see arez.annotations.Priority#HIGH 1037 * @see arez.spy.Priority#HIGH 1038 * @see Task.Flags#PRIORITY_HIGH 1039 */ 1040 public static final int PRIORITY_HIGH = 0b010 << 15; 1041 /** 1042 * Normal priority if no other priority otherwise specified. 1043 * <p>Only one of the PRIORITY_* flags should be applied to observer.</p> 1044 * 1045 * @see arez.annotations.Priority#NORMAL 1046 * @see arez.spy.Priority#NORMAL 1047 * @see Task.Flags#PRIORITY_NORMAL 1048 */ 1049 public static final int PRIORITY_NORMAL = 0b011 << 15; 1050 /** 1051 * Low priority. 1052 * Usually used to schedule observers that reflect state onto non-reactive 1053 * application components. i.e. Observers that are used to build html views, 1054 * perform network operations etc. These reactions are often at low priority 1055 * to avoid recalculation of dependencies (i.e. {@link ComputableValue}s) triggering 1056 * this reaction multiple times within a single reaction round. 1057 * <p>Only one of the PRIORITY_* flags should be applied to observer.</p> 1058 * 1059 * @see arez.annotations.Priority#LOW 1060 * @see arez.spy.Priority#LOW 1061 * @see Task.Flags#PRIORITY_LOW 1062 */ 1063 public static final int PRIORITY_LOW = 0b100 << 15; 1064 /** 1065 * Lowest priority. Use this priority if the observer is a {@link ComputableValue} that 1066 * may be unobserved when a {@link #PRIORITY_LOW} observer reacts. This is used to avoid 1067 * recomputing state that is likely to either be unobserved or recomputed as part of 1068 * another observers reaction. 1069 * <p>Only one of the PRIORITY_* flags should be applied to observer.</p> 1070 * 1071 * @see arez.annotations.Priority#LOWEST 1072 * @see arez.spy.Priority#LOWEST 1073 * @see Task.Flags#PRIORITY_LOWEST 1074 */ 1075 public static final int PRIORITY_LOWEST = 0b101 << 15; 1076 /** 1077 * Mask used to extract priority bits. 1078 */ 1079 public static final int PRIORITY_MASK = 0b111 << 15; 1080 /** 1081 * The observer can only read arez state. 1082 */ 1083 public static final int READ_ONLY = 1 << 24; 1084 /** 1085 * The observer can read or write arez state. 1086 */ 1087 public static final int READ_WRITE = 1 << 23; 1088 /** 1089 * The scheduler will be triggered when the observer is created to immediately invoke the 1090 * {@link Observer#_observe} function. This configuration should not be specified if there 1091 * is no {@link Observer#_observe} function supplied. This should not be 1092 * specified if {@link #RUN_LATER} is specified. 1093 */ 1094 @SuppressWarnings( "WeakerAccess" ) 1095 public static final int RUN_NOW = 1 << 22; 1096 /** 1097 * The scheduler will not be triggered when the observer is created. The observer either 1098 * has no {@link Observer#_observe} function or is responsible for ensuring that 1099 * {@link ArezContext#triggerScheduler()} is invoked at a later time. This should not be 1100 * specified if {@link #RUN_NOW} is specified. 1101 */ 1102 public static final int RUN_LATER = 1 << 21; 1103 /** 1104 * Flag indicating that the Observer is allowed to observe {@link ComputableValue} instances with a lower priority. 1105 */ 1106 public static final int OBSERVE_LOWER_PRIORITY_DEPENDENCIES = 1 << 30; 1107 /** 1108 * Indicates that the an action can be created from within the Observers observed function. 1109 */ 1110 public static final int NESTED_ACTIONS_ALLOWED = 1 << 29; 1111 /** 1112 * Indicates that the an action must not be created from within the Observers observed function. 1113 */ 1114 public static final int NESTED_ACTIONS_DISALLOWED = 1 << 28; 1115 /** 1116 * Flag set set if the application code can not invoke the {@link Observer#reportStale()} method. 1117 * 1118 * @see arez.annotations.DepType#AREZ 1119 */ 1120 public static final int AREZ_DEPENDENCIES = 1 << 27; 1121 /** 1122 * Flag set set if the application code can not invokethe {@link Observer#reportStale()} method to indicate 1123 * that a dependency has changed. In this scenario it is not an error if the observer does not invoke the 1124 * {@link ObservableValue#reportObserved()} on a dependency during it's reaction. 1125 * 1126 * @see arez.annotations.DepType#AREZ_OR_NONE 1127 */ 1128 public static final int AREZ_OR_NO_DEPENDENCIES = 1 << 26; 1129 /** 1130 * Flag set if the application code can invoke the {@link Observer#reportStale()} method to indicate that a non-arez dependency has changed. 1131 * 1132 * @see arez.annotations.DepType#AREZ_OR_EXTERNAL 1133 */ 1134 public static final int AREZ_OR_EXTERNAL_DEPENDENCIES = 1 << 25; 1135 /** 1136 * The runtime will keep the observer reacting to dependencies until disposed. This is the default value for 1137 * observers that supply a observed function. 1138 */ 1139 public static final int KEEPALIVE = 1 << 20; 1140 /** 1141 * Mask used to extract dependency type bits. 1142 */ 1143 private static final int DEPENDENCIES_TYPE_MASK = 1144 AREZ_DEPENDENCIES | AREZ_OR_NO_DEPENDENCIES | AREZ_OR_EXTERNAL_DEPENDENCIES; 1145 /** 1146 * Mask to extract "NESTED_ACTIONS" option so can derive default value if required. 1147 */ 1148 private static final int NESTED_ACTIONS_MASK = NESTED_ACTIONS_ALLOWED | NESTED_ACTIONS_DISALLOWED; 1149 /** 1150 * Flag indicating whether next scheduled invocation of {@link Observer} should invoke {@link Observer#_observe} or {@link Observer#_onDepsChange}. 1151 */ 1152 static final int EXECUTE_OBSERVE_NEXT = 1 << 9; 1153 /** 1154 * Mask used to extract state bits. 1155 * State is the lowest bits as it is the most frequently accessed numeric fields 1156 * and placing values at lower part of integer avoids a shift. 1157 */ 1158 static final int STATE_MASK = 0b111; 1159 /** 1160 * Mask that identifies the bits associated with runtime configuration. 1161 */ 1162 static final int RUNTIME_FLAGS_MASK = EXECUTE_OBSERVE_NEXT | STATE_MASK; 1163 /** 1164 * The observer has been disposed. 1165 */ 1166 static final int STATE_DISPOSED = 0b001; 1167 /** 1168 * The observer is in the process of being disposed. 1169 */ 1170 static final int STATE_DISPOSING = 0b010; 1171 /** 1172 * The observer is not active and is not holding any data about it's dependencies. 1173 * Typically mean this tracker observer has not been run or if it is a ComputableValue that 1174 * there is no observer observing the associated ObservableValue. 1175 */ 1176 static final int STATE_INACTIVE = 0b011; 1177 /** 1178 * No change since last time observer was notified. 1179 */ 1180 static final int STATE_UP_TO_DATE = 0b100; 1181 /** 1182 * A transitive dependency has changed but it has not been determined if a shallow 1183 * dependency has changed. The observer will need to check if shallow dependencies 1184 * have changed. Only Derived observables will propagate POSSIBLY_STALE state. 1185 */ 1186 static final int STATE_POSSIBLY_STALE = 0b101; 1187 /** 1188 * A dependency has changed so the observer will need to recompute. 1189 */ 1190 static final int STATE_STALE = 0b110; 1191 /** 1192 * The flag is valid on observers associated with computable values and will deactivate the observer if the 1193 * computable value has no observers. 1194 */ 1195 static final int DEACTIVATE_ON_UNOBSERVE = 1 << 19; 1196 /** 1197 * The flag is valid on observers where the observed function is invoked by the application. 1198 */ 1199 static final int APPLICATION_EXECUTOR = 1 << 18; 1200 /** 1201 * Mask used to extract react type bits. 1202 */ 1203 static final int SCHEDULE_TYPE_MASK = KEEPALIVE | DEACTIVATE_ON_UNOBSERVE | APPLICATION_EXECUTOR; 1204 /** 1205 * Mask that identifies the bits associated with static configuration. 1206 */ 1207 static final int CONFIG_FLAGS_MASK = 1208 PRIORITY_MASK | 1209 RUN_NOW | RUN_LATER | 1210 READ_ONLY | READ_WRITE | 1211 OBSERVE_LOWER_PRIORITY_DEPENDENCIES | 1212 NESTED_ACTIONS_MASK | 1213 DEPENDENCIES_TYPE_MASK | 1214 SCHEDULE_TYPE_MASK | 1215 NO_REPORT_RESULT; 1216 1217 /** 1218 * Extract and return the observer's state. 1219 * 1220 * @param flags the flags. 1221 * @return the state. 1222 */ 1223 static int getState( final int flags ) 1224 { 1225 return flags & STATE_MASK; 1226 } 1227 1228 /** 1229 * Return the new value of flags when supplied with specified state. 1230 * 1231 * @param flags the flags. 1232 * @param state the new state. 1233 * @return the new flags. 1234 */ 1235 static int setState( final int flags, final int state ) 1236 { 1237 return ( flags & ~STATE_MASK ) | state; 1238 } 1239 1240 /** 1241 * Return true if the state is UP_TO_DATE, POSSIBLY_STALE or STALE. 1242 * The inverse of {@link #isNotActive(int)} 1243 * 1244 * @param flags the flags to check. 1245 * @return true if the state is UP_TO_DATE, POSSIBLY_STALE or STALE. 1246 */ 1247 static boolean isActive( final int flags ) 1248 { 1249 return getState( flags ) > STATE_INACTIVE; 1250 } 1251 1252 /** 1253 * Return true if the state is INACTIVE, DISPOSING or DISPOSED. 1254 * The inverse of {@link #isActive(int)} 1255 * 1256 * @param flags the flags to check. 1257 * @return true if the state is INACTIVE, DISPOSING or DISPOSED. 1258 */ 1259 static boolean isNotActive( final int flags ) 1260 { 1261 return !isActive( flags ); 1262 } 1263 1264 /** 1265 * Return the least stale observer state. if the state is not active 1266 * then the {@link #STATE_UP_TO_DATE} will be returned. 1267 * 1268 * @param flags the flags to check. 1269 * @return the least stale observer state. 1270 */ 1271 static int getLeastStaleObserverState( final int flags ) 1272 { 1273 final int state = getState( flags ); 1274 return state > STATE_INACTIVE ? state : STATE_UP_TO_DATE; 1275 } 1276 1277 /** 1278 * Return the state as a string. 1279 * 1280 * @param state the state value. One of the STATE_* constants 1281 * @return the string describing state. 1282 */ 1283 @Nonnull 1284 static String getStateName( final int state ) 1285 { 1286 assert Arez.shouldCheckInvariants() || Arez.shouldCheckApiInvariants(); 1287 //noinspection EnhancedSwitchMigration 1288 switch ( state ) 1289 { 1290 case STATE_DISPOSED: 1291 return "DISPOSED"; 1292 case STATE_DISPOSING: 1293 return "DISPOSING"; 1294 case STATE_INACTIVE: 1295 return "INACTIVE"; 1296 case STATE_POSSIBLY_STALE: 1297 return "POSSIBLY_STALE"; 1298 case STATE_STALE: 1299 return "STALE"; 1300 case STATE_UP_TO_DATE: 1301 return "UP_TO_DATE"; 1302 default: 1303 return "UNKNOWN(" + state + ")"; 1304 } 1305 } 1306 1307 static int nestedActionRule( final int flags ) 1308 { 1309 return Arez.shouldCheckInvariants() ? 1310 0 != ( flags & NESTED_ACTIONS_MASK ) ? 0 : NESTED_ACTIONS_DISALLOWED : 1311 0; 1312 } 1313 1314 /** 1315 * Return true if flags contains a valid nested action mode. 1316 * 1317 * @param flags the flags. 1318 * @return true if flags contains valid nested action mode. 1319 */ 1320 static boolean isNestedActionsModeValid( final int flags ) 1321 { 1322 return NESTED_ACTIONS_ALLOWED == ( flags & NESTED_ACTIONS_ALLOWED ) ^ 1323 NESTED_ACTIONS_DISALLOWED == ( flags & NESTED_ACTIONS_DISALLOWED ); 1324 } 1325 1326 /** 1327 * Return the default dependency type flag if dependency type not specified. 1328 * 1329 * @param flags the flags. 1330 * @return the default dependency type if dependency type unspecified else 0. 1331 */ 1332 static int dependencyType( final int flags ) 1333 { 1334 return Arez.shouldCheckInvariants() ? 0 != ( flags & DEPENDENCIES_TYPE_MASK ) ? 0 : AREZ_DEPENDENCIES : 0; 1335 } 1336 1337 /** 1338 * Extract and return the schedule type. 1339 * 1340 * @param flags the flags. 1341 * @return the schedule type. 1342 */ 1343 static int getScheduleType( final int flags ) 1344 { 1345 return flags & SCHEDULE_TYPE_MASK; 1346 } 1347 1348 /** 1349 * Return true if flags contains a valid ScheduleType. 1350 * 1351 * @param flags the flags. 1352 * @return true if flags contains a valid ScheduleType. 1353 */ 1354 static boolean isScheduleTypeValid( final int flags ) 1355 { 1356 return KEEPALIVE == ( flags & KEEPALIVE ) ^ 1357 DEACTIVATE_ON_UNOBSERVE == ( flags & DEACTIVATE_ON_UNOBSERVE ) ^ 1358 APPLICATION_EXECUTOR == ( flags & APPLICATION_EXECUTOR ); 1359 } 1360 } 1361}