File: README.close_semantics

package info (click to toggle)
libnb-platform18-java 12.1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 729,800 kB
  • sloc: java: 5,059,097; xml: 574,432; php: 78,788; javascript: 29,039; ansic: 10,278; sh: 6,386; cpp: 4,612; jsp: 3,643; sql: 1,097; makefile: 540; objc: 288; perl: 277; haskell: 93
file content (249 lines) | stat: -rw-r--r-- 9,012 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

"README.close_semantics"

Semantics of InputOutput (IO) close operations
	or
Major state transitions of an IO.

TOC
---
Introduction
Motivation
Dramatis personae
Major state transitions
Typical lifecycle
Querying the state
Misc. notes on IOContainer

Introduction
------------
This writeup evolved in an attempt to analyze the existing default
implementation (output2) while implementing a second provider (terminal).
It is written as a "best effort" and undoubtedly still contains errors 
regarding the default 'output2' provider.

It is primarily intended for implementors/maintainers but should be useful
to API users as well.

While the general analysis pertains to both the 'output2' and the 'terminal'
providers, some methods are only available for terminal.

Motivation
----------
The I/O APIs contain many methods and user visible actions with the
word "close" in them:
	InputOutput.closeInputOutput().
	The Close action in the popup menu.
	The Close Tab/Close All Tabs/Close Other Tabs actions.
	IOContainer.CallBacks.closed().
	OutputWriter.close() (inherited from PrintWriter).

There also exist various predicates like:
	InputOutput.isClosed().
	IOContainer.isClosable().

There seems to be no corresponding "open" operation.
Instead we have various gets's and select's:
	IOProvider.getIO()
	InputOutput.getOut().

	InputOutput.select().
	IOContainer.select().

It turns out that methods based on the words "close" or "select" don't
all mean or do the same thing.

Dramatis personae
-----------------
IOProvider	Has a list of IO's
InputOutput	Abstract entity owned by an IOProvider
		Has Streams.
		Has a "Component".
IOContainer Abstract entity.
		Has a list of Components that are visible.
		The actual container implementation is hidden.
Component	Concrete entity (Typically a JComponent) that appears in an
	    IOContainer. (IOTab in output2/Terminal in terminal).

Major state transitions
-----------------------
There are three major state "variables" that control the overall state
of an IO:

- Existence
  An IO comes into existence via one of the family IOProvider.getIO().

  Calling IO.closeInputOutput() disposes it. It is of course still available
  until it is GC'ed but any operation on it after closeInputOutput() is
  undefined.

  Both output2 and terminal use the words dispose[d] internally. So
  we'll adopt dispose for our terminology.

  Disposal frees all resources used by an IO and additionally calls eof()
  in IO.getIn().

  There is no associated UI action for disposal so clients need to ensure
  that they call closeInputOutput() when appropriate (heh-heh).

  Disposing was called "Strong closing" in some earlier discussions.

- Visibility
  An IO is visible when it is visible in it's container (with or without a tab)
  _regardless_ of whether the container is visible as a window. I.e.
  an IO tab may be added to the Output window even though the output
  window itself is closed.

  An IO can be made visible (opened?) using ...
  o IO.select()
    Also ensures that the container (it's TopComponent) is visible.
  o IOSelect.select() which allows finer control over the visibility
    of the containing TopComponent.
  o IOVisibility.setVisible(true)
    This one is independent of the visibility of the container itself.

  A freshly created IO is visible by default but not selected! That is,
  it is added to a container but the TopComponent will not be made
  visible.

  An IO can be made invisible (closed) using ...
  o Close Action in context menu
  o Clicking on the X of it's tab.
  o IOVisibility.setVisible(false)

  By far the most common use of the term "close" applies to this
  state transition so we'll adopt close for our terminology.

  This kind of closing was called "Weak closing" in some earlier discussions.

  Conditional vs Unconditional closing
  ....................................
  The following closing operations ...
  o Close Action in context menu
  o Clicking on the X of it's tab.
  are tempered using IOVisibility.setClosable() and additionally via
  the vetoing mechanism provided by IONotifier.addVetoableChangeListener()
  and IOVisibility.PROP_VISIBILITY.

  The following closing operations succeed unconditionally:
  o IOVisibility.setVisible(false)
  o InputOutput.closeInputOutput()

- Connectedness
  An IO starts life in a disconnected state.

  An IO is connected if one of IO.getOut(), IO.getErr() or IOTerm.connect()
  are used.
  A connected IO has it's title rendered in bold.

  An IO is disconnected if the OutputWriters returned by getOut() and
  getIn() are close()ed and once IOTerm.disconnect()'s continuation is called.
  As a convenience closing getOut() will close getErr().

  	Not quite sure about this: With the output2 provider
	an IO can go back to the connected state after a reset() and/or
	the first write.

	With the terminal provider IO goes back to connected state 
	as explained above.

  The use of the word close in this context is highly misleading because even
  after closing them one can write to the OutputWriters provided by getOut()
  and getErr()!

  It's probably best to never save the OutputWriter returned by getOut/Err()
  and call getOut/Err() every time. That will ensure the correct maintenance
  of the connected state.

  So, if close() doesn't really close anything, what does it really mean to be
  disconnected? It serves two purposes:

  o A disconnected IO is eligible for reuse via
    	IOProvider.getIO(String name, boolean newIO = false)
    provided the names match.
    This is the original and primary use of this state.
    The precise semantics of reuse are as follows:
	- choose a set of disconnecetd IO's with matching names.
	- if any are found close all but one and return it.
	- else return null

  o IONotifier.addVetoableChangeListener() together with
    IOVisibility.PROP_VISIBLE provide a way of accidentally preventing the
    closure (setVisible(false)) of an IO which cannot be made visible again.

  A disconnected IO retains it's buffer so if it's is to be reused, 
  IO.getOut().reset() should be used to clear the buffer.

  Disconnecting was called "Stream closing" in some earlier discussions.
  [ The output2 implementation seems to track this state in two ways:
    - OutputWriter.isClosed().
    - An internal property streamClosed. ]

The visibility and connected states are independent of each other. All
four combinations make sense and are allowed.

A disposed (IO.closeInputOutput()) IO becomes invisible and is disconnected.
It is not eligible for reuse via IOProvider.getIO(..., newIO = false).
	- output2 allows a disposed IO to be re-select()ed.
	- terminal ignores all operations on a dispose IO.

Typical lifecycle
-----------------

	IOProvider ioProvider = IOProvider.getDefault();
	InputOutput io = ioProvider.getIO("name", null);
	io.select();

	OutputWriter ow = io.getOut();
	ow.println("Hello");

	ow.close();				// optional
	if (IOVisibility.isSupported(IO))
		IOVisibility.setVisible(false);	// optional
	io.closeInputOutptu();


Querying the state
------------------
Operations on an IO may be requested on any thread. However, they
are mostly performed on the EDT. As a result the state transition will
not happen serially with request to the request!

Querying of state, e.g. using IOVisibility.isClosable(), works as follows:
If the call is performed on the EDT the result is returned immediately.
Otherwise the call is blocked while the result is computed on the EDT
and passed back via a Future.

Misc. notes on IOContainer
--------------------------
Although IOContainer is part of the API it is primarily intended for use
by implementations (analog of a protected final method).

- IOContainer.remove() is used to implement closing (setVisible(false)).
  It removes the Component and calls IOContainer.CallBacks.closed().

  Because a disconnected IO may be reused, CallBacks.closed() should not
  free any resources! [ output2 impl tries some heuristics and sometimes
  frees stuff anyway ].

- What about IOContainer[.Provider].isClosable()?
  It's original semantics are unclear.
  The 'output2' provider always returns true.
  In the 'terminal' provider I took it to mean "can the component
  be made invisible"?