@@ -0,0 +1,181 @@
1+Adding Support for Multicast to the Transport API
2+=================================================
4+This document describes various design options for adding support for multicast
7+Creating a new multicast group
8+------------------------------
10+We can either have this as part of the transport
12+ data Transport = Transport {
14+ , newMulticastGroup :: IO (Either Error MulticastGroup)
17+ data MulticastGroup = MulticastGroup {
19+ , multicastAddress :: MulticastAddress
20+ , deleteMulticastGroup :: IO ()
23+or as part of an endpoint:
25+ data Transport = Transport {
26+ newEndPoint :: IO (Either Error EndPoint)
29+ data EndPoint = EndPoint {
31+ , newMulticastGroup :: IO (Either Error MulticastGroup)
34+It should probably be part of the `Transport`, as there is no real connection
35+between an endpoint and the creation of the multigroup (however, see section
36+"Sending messages to a multicast group").
38+Subscribing to a multicast group
39+--------------------------------
41+This should be part of an endpoint; subscribing basically means that the
42+endpoint wants to receive events when multicast messages are sent.
44+We could reify a subscription:
46+ data EndPoint = EndPoint {
48+ , multicastSubscribe :: MulticastAddress -> IO MulticastSubscription
51+ data MulticastSubscription = MulticastSubscription {
53+ , multicastSubscriptionClose :: IO ()
56+but this suggests that one might have multiple subscriptions to the same group
57+which can be distinguished, which is misleading. Probably better to have:
59+ data EndPoint = EndPoint {
60+ multicastSubscribe :: MulticastAddress -> IO ()
61+ , multicastUnsubscribe :: MulticastAddress -> IO ()
64+Sending messages to a multicast group
65+-------------------------------------
67+An important feature of the Transport API is that we are clear about which
68+operations are *lightweight* and which are not. For instance, creating new
69+endpoints is not lightweight, but opening new connections to endpoints is (as
70+light-weight as possible).
72+Clearly the creation of a new multicast group is a heavyweight operation. It is
73+less evident however if we can support multiple lightweight "connections" to the
74+same multicast group, and if so, whether it is useful.
76+If we decide that multiple lightweight connections to the multigroup is useful,
79+ data EndPoint = EndPoint {
81+ , connect :: Address -> Reliability -> IO (Either Error Connection)
82+ , multicastConnect :: MulticastAddress -> IO (Either Error Connection)
85+ data Connection = Connection {
86+ connectionId :: ConnectionId
87+ , send :: [ByteString] -> IO ()
89+ , maxMsgSize :: Maybe Int
93+ Receive ConnectionId [ByteString]
94+ | ConnectionClosed ConnectionId
95+ | ConnectionOpened ConnectionId ConnectionType Reliability Address
97+The advantage of this approach is it's consistency with the rest of the
98+interface. The problem is that with multicast we cannot reliably send any
99+control messages, so we cannot make sure that the subscribers of the multicast
100+group will receive ConnectionOpened events when an endpoint creates a new
101+connection. Since we don't support these "connectionless connections" anywhere
102+else in the API this seems inconsistent with the rest of the design (this
103+implies that an "unreliable" Transport over UDP still needs to have reliable
104+control messages). (On the other hand, if we were going to support reliable
105+multicast protocols, then that would fit this design).
107+If we don't want to support multiple lightweight connections to a multicast
108+group then a better design would be
110+ data EndPoint = EndPoint {
111+ , connect :: Address -> Reliability -> IO (Either Error Connection)
112+ , multicastSend :: MulticastAddress -> [ByteString] -> IO ()
117+ | MulticastReceive Address [ByteString]
121+ data EndPoint = EndPoint {
123+ , resolveMulticastGroup :: MulticastAddress -> IO (Either Error MulticastGroup)
126+ data MulticastGroup = MulticastGroup {
128+ , send :: [ByteString] -> IO ()
131+If we do this however we need to make sure that newGroup is part an `EndPoint`,
132+not the `Transport`, otherwise `send` will not know the source of the message.
133+The version with `resolveMulticastGroup` has the additional benefit that in
134+"real" implementations we will probably need to allocate some resources before
135+we can send to the multicast group, and need to deallocate these resources at
141+The above considerations lead to the following tentative proposal:
143+ data Transport = Transport {
144+ newEndPoint :: IO (Either Error EndPoint)
147+ data EndPoint = EndPoint {
149+ , address :: Address
150+ , connect :: Address -> Reliability -> IO (Either Error Connection)
151+ , newMulticastGroup :: IO (Either Error MulticastGroup)
152+ , resolveMulticastGroup :: MulticastAddress -> IO (Either Error MulticastGroup)
155+ data Connection = Connection {
156+ send :: [ByteString] -> IO ()
161+ Receive ConnectionId [ByteString]
162+ | ConnectionClosed ConnectionId
163+ | ConnectionOpened ConnectionId Reliability Address
164+ | MulticastReceive MulticastAddress [ByteString]
166+ data MulticastGroup = MulticastGroup {
167+ multicastAddress :: MulticastAddress
168+ , deleteMulticastGroup :: IO ()
169+ , maxMsgSize :: Maybe Int
170+ , multicastSend :: [ByteString] -> IO ()
171+ , multicastSubscribe :: IO ()
172+ , multicastUnsubscribe :: IO ()
173+ , multicastClose :: IO ()
176+where `multicastClose` indicates to the runtime that this endpoint no longer
177+wishes to send to this multicast group, and we can therefore deallocate the
178+resources we needed to send to the group (these resources can be allocated on
179+`resolveMulticastGroup` or on the first `multicastSend`; the advantage of the
180+latter is that is somebody resolves a group only to subscribe to it, not to
181+send to it, we don't allocate any unneeded resources).