text: //
  //  RACSerialDisposable.m
  //  ReactiveCocoa
  //
  //  Created by Justin Spahr-Summers on 2013-07-22.
  //  Copyright (c) 2013 GitHub, Inc. All rights reserved.
  //
  
  #import "RACSerialDisposable.h"
  #import <libkern/OSAtomic.h>
  
  @interface RACSerialDisposable () {
  	// A reference to the receiver's `disposable`. This variable must only be
  	// modified atomically.
  	//
  	// If this is `self`, no `disposable` has been set, but the receiver has not
  	// been disposed of yet. `self` is never stored retained.
  	//
  	// If this is `nil`, the receiver has been disposed.
  	//
  	// Otherwise, this is a retained reference to the inner disposable and the
  	// receiver has not been disposed of yet.
  	void * volatile _disposablePtr;
  }
  
  @end
  
  @implementation RACSerialDisposable
  
  #pragma mark Properties
  
  - (BOOL)isDisposed {
  	return _disposablePtr == nil;
  }
  
  - (RACDisposable *)disposable {
  	RACDisposable *disposable = (__bridge id)_disposablePtr;
  	return (disposable == self ? nil : disposable);
  }
  
  - (void)setDisposable:(RACDisposable *)disposable {
  	[self swapInDisposable:disposable];
  }
  
  #pragma mark Lifecycle
  
  + (instancetype)serialDisposableWithDisposable:(RACDisposable *)disposable {
  	RACSerialDisposable *serialDisposable = [[self alloc] init];
  	serialDisposable.disposable = disposable;
  	return serialDisposable;
  }
  
  - (id)init {
  	self = [super init];
  	if (self == nil) return nil;
  
  	_disposablePtr = (__bridge void *)self;
  	OSMemoryBarrier();
  
  	return self;
  }
  
  - (id)initWithBlock:(void (^)(void))block {
  	self = [self init];
  	if (self == nil) return nil;
  
  	self.disposable = [RACDisposable disposableWithBlock:block];
  
  	return self;
  }
  
  - (void)dealloc {
  	self.disposable = nil;
  }
  
  #pragma mark Inner Disposable
  
  - (RACDisposable *)swapInDisposable:(RACDisposable *)newDisposable {
  	void * const selfPtr = (__bridge void *)self;
  
  	// Only retain the new disposable if it's not `self`.
  	// Take ownership before attempting the swap so that a subsequent swap
  	// receives an owned reference.
  	void *newDisposablePtr = selfPtr;
  	if (newDisposable != nil) {
  		newDisposablePtr = (void *)CFBridgingRetain(newDisposable);
  	}
  
  	void *existingDisposablePtr;
  	// Keep trying while we're not disposed.
  	while ((existingDisposablePtr = _disposablePtr) != NULL) {
  		if (!OSAtomicCompareAndSwapPtrBarrier(existingDisposablePtr, newDisposablePtr, &_disposablePtr)) {
  			continue;
  		}
  
  		// Return nil if _disposablePtr was set to self. Otherwise, release
  		// the old value and return it as an object.
  		if (existingDisposablePtr == selfPtr) {
  			return nil;
  		} else {
  			return CFBridgingRelease(existingDisposablePtr);
  		}
  	}
  
  	// At this point, we've found out that we were already disposed.
  	[newDisposable dispose];
  
  	// Failed to swap, clean up the ownership we took prior to the swap.
  	if (newDisposable != nil) {
  		CFRelease(newDisposablePtr);
  	}
  
  	return nil;
  }
  
  #pragma mark Disposal
  
  - (void)dispose {
  	void *existingDisposablePtr;
  
  	while ((existingDisposablePtr = _disposablePtr) != NULL) {
  		if (OSAtomicCompareAndSwapPtrBarrier(existingDisposablePtr, NULL, &_disposablePtr)) {
  			if (existingDisposablePtr != (__bridge void *)self) {
  				RACDisposable *existingDisposable = CFBridgingRelease(existingDisposablePtr);
  				[existingDisposable dispose];
  			}
  
  			break;
  		}
  	}
  }
  
  @end
  