aboutsummaryrefslogtreecommitdiffstats
path: root/actioncable/lib/action_cable/channel/base.rb
diff options
context:
space:
mode:
authorpalkan <dementiev.vm@gmail.com>2016-09-19 12:29:23 +0300
committerpalkan <dementiev.vm@gmail.com>2016-09-20 19:42:24 +0300
commit03a209e92aeed1e724b3ff787ec77936b7163ca5 (patch)
treecba7736f79df8686f1906ed7d9316c00afd02c97 /actioncable/lib/action_cable/channel/base.rb
parent254db4987d03d3c9d3fca1e2961da8fd7dd0e06b (diff)
downloadrails-03a209e92aeed1e724b3ff787ec77936b7163ca5.tar.gz
rails-03a209e92aeed1e724b3ff787ec77936b7163ca5.tar.bz2
rails-03a209e92aeed1e724b3ff787ec77936b7163ca5.zip
[Fix #25381] Avoid race condition on subscription confirmation
Diffstat (limited to 'actioncable/lib/action_cable/channel/base.rb')
-rw-r--r--actioncable/lib/action_cable/channel/base.rb26
1 files changed, 18 insertions, 8 deletions
diff --git a/actioncable/lib/action_cable/channel/base.rb b/actioncable/lib/action_cable/channel/base.rb
index 2e589a2cfa..1d9038b76a 100644
--- a/actioncable/lib/action_cable/channel/base.rb
+++ b/actioncable/lib/action_cable/channel/base.rb
@@ -144,7 +144,9 @@ module ActionCable
# When a channel is streaming via pubsub, we want to delay the confirmation
# transmission until pubsub subscription is confirmed.
- @defer_subscription_confirmation = false
+ #
+ # We use atomic fixnum to track the number of waiting tasks to avoid race conditions
+ @defer_subscription_confirmation_counter = Concurrent::AtomicFixnum.new(1)
@reject_subscription = nil
@subscription_confirmation_sent = nil
@@ -153,6 +155,14 @@ module ActionCable
subscribe_to_channel
end
+ # This method is called after subscription has been added to the channel.
+ # Send confirmation here to avoid race conditions when client tries to perform actions
+ # right after receiving confirmation.
+ def registered!
+ @defer_subscription_confirmation_counter.decrement
+ transmit_subscription_confirmation unless defer_subscription_confirmation?
+ end
+
# Extract the action name from the passed data and process it via the channel. The process will ensure
# that the action requested is a public method on the channel declared by the user (so not one of the callbacks
# like #subscribed).
@@ -202,17 +212,21 @@ module ActionCable
end
def defer_subscription_confirmation!
- @defer_subscription_confirmation = true
+ @defer_subscription_confirmation_counter.increment
end
def defer_subscription_confirmation?
- @defer_subscription_confirmation
+ @defer_subscription_confirmation_counter.value.positive?
end
def subscription_confirmation_sent?
@subscription_confirmation_sent
end
+ def registered?
+ @registered
+ end
+
def reject
@reject_subscription = true
end
@@ -235,11 +249,7 @@ module ActionCable
subscribed
end
- if subscription_rejected?
- reject_subscription
- else
- transmit_subscription_confirmation unless defer_subscription_confirmation?
- end
+ reject_subscription if subscription_rejected?
end
def extract_action(data)