On Mar 31, 2:14 pm, Paul Sprangers <
P...@sprie.nl> wrote:
> In article <
mpro.m1r1kn05pejpc01nm.n...@stevefryatt.org.uk>,
> Steve Fryatt <
n...@stevefryatt.org.uk> wrote:
>
> > > IF i% MOD100 = 0, THEN SYS"Wimp_Poll",&xxxx,block%
> > IF i% MOD100 = 0, THEN PROCpoll_wimp
>
> I fully agree that calling the poll routine is much better and certainly
> more flexible than just a calling SYS"Wimp_Poll". However, if there's no
> need for the program to react on reason codes while processing a certain
> task, such as the one in the example, you may as well just return control
> to other applications.
[slight aside]
Doing things based on a number of iterations doesn't help the user
interface that much. It may be more sensible to limit the processing
time that you run for, rather than by a number of iterations. For
example, on a given system, 100 iterations may make for a smooth
desktop, whereas on a slower system 100 iterations may take longer and
so make the desktop sluggish to use. On a faster system, 100
iterations may be fast but the overhead of switching in and out
becomes more significant and so the application doesn't run
proportionally as fast as the machine. Limitations may exist in the
operations you perform, such as FS operations may be very slow on some
systems, so you cannot do (say) 100 of them in the same time as you
expect, resulting in a sluggish system.
It's not always right to use iterations as your limiting factor -
adding a time factor in there may also help, eg:
IF processed_this_iteration > 100 OR time_elapsed_this_iteration >
15 THEN
yield to desktop
ENDIF
(or similar - doing the yield inside the loop is against my better
judgement albeit simpler)
> > For example, if you're processing a batch of files, what do you do if
> > the user drags some more to the iconbar (eg, do you add them to your
> > queue, or do you grey out the iconbar icon)?
>
> Since the program won't react on reason codes at all (at least, when using
> the straight SYS"Wimp_Poll" call), the extra drag will simply be ignored.
> That's at least what I'm experiencing in a similar case.
> Meanwhile, you can still get access to other applications.
>
> So, as far as I can see (agreed, not very far), there's not always a need
> for calling the entire poll routine. A simple SYS"Wimp_Poll" can sometimes
> do the trick as well - although I do feel that this might be a way of dirty
> programming.
The gain you make from creating a separate, subset, of the poll entry
points is massively outweighed by the maintenance and testing costs,
and the variation in user experience that you will see from doing so.
In short, you're going to regret it.
Firstly you have to identify the calls which you can safely handle in
your limited entry point. This may not be difficult initially as you
probably only handle a few poll reasons, but will become increasingly
so as you add functionality. Someone else has already mentioned the
redraw handling which must be performed. By having a subset of your
poll codes duplicated, you find you need to test more - whilst a
process is being run, and whilst a process is not being run.
Of course you could implement your poll routine conditionally, such
that every poll reason, or message, checks whether the system is
active or not and changes its behaviour accordingly, eg
WHEN reason_mouseclick
IF processing THEN
...
ELSE
...
ENDIF
This is probably reasonable, and however you implemented things you
might need to do the same thing, but it does add a maintenance burden.
Particular things you need to ensure that you get right if you're
going to handle your polls in separate places:
* Does Quit from the TaskManager work properly?
There are two messages - the PreQuit query, and the final Quit.
You should not
assume you will receive a query, but if you're in the middle of
processing,
you need to react and probably ask the user.
Remember - you cannot just exit when you receive the Quit message;
you'll
need to set a flag to indicate that you're exiting. You probably
do this anyhow,
in order to clean up resources, but now you're in the middle of a
call to the
processing routine. You will need to check that flag inside your
loop that is
performing the processing so that you can finish the it tidily,
close files,
release resources, and return to its caller properly, so that you
exit cleanly.
* Does quit from the iconbar menu (or similar) work properly?
Does the menu even work - you have to handle the mouseclicks to
open the menu
in the first place, and the menu selection to act on it.
Of course, you will have already had to ensure that menu items
that are not
selectable are greyed out whilst whatever operation is being
performed is
happening. And that should be *just* those that don't make sense -
for example
a 'start processing' option would make no sense whilst you're
already running,
and clicking it would be confusing
(Windows XP has a Zip decompress window which, during the
decompress leaves
the 'Next' button available after you've started the
decompressing, which can
let you press it twice - possibly this restarts the decompression,
or maybe
it starts a second thread that continues decompression whilst the
first is still
running... whatever is the case, there's poor logic there that
someone hasn't
considered - it's good that it doesn't crash when it's put into
that state, but
maybe that's luck, and its better to consider the problem than to
leave the
confusion to the user).
* Does user redraw still work ?
Already been mentioned - this is always an issue.
* Does moving windows work ?
Equally important, as it is frustrating for a user to find that
windows are stuck
on the screen in fixed positions because the author never
considered that whilst
one application was running you might want to work with another.
Remember that
'moving' includes placing the windows in the foreground and
background.
There is a secondary to this in that any panes attached to windows
have to move
as well - if you're using the nested wimp, this happens for free,
but if you're
doing it old-school (because the nested wimp doesn't offer exactly
the features
you need, or because you're working on an OS that's more than 12
years old) you
have to worry about that.
* Does closing the windows work ?
Not only work, but make sense. If you close the window that is
showing the
processing status, you need to be sure that that window can be
opened again, or
whether that cancels the operation. If your processing was to
import into a
document window and closing that window will remove the document,
you need to
be sure that the document is closed and the processing stops
(obviously safely!)
* Window/Icon entering/leaving must still be honoured.
If you're changing pointer, or performing user redraw based on
pointer positions
tracked by the window/icon enter/leave reasons you need to keep
these working,
as otherwise you're going to find that the pointer is stuck in the
wrong shape.
* Do !Help messages still work?
If they don't then you've got an inconsistent interface to users
that might be
confusing.
* Data transfer operations should be honoured.
This is the more fun one that you'll have to handle for the case
stated in the
OP. If 5 files are dropped on the application, the first one will
be delivered
and will start the processing. The processing loop then calls the
poll, which
then delivers the second of the 5 files. At this point if the poll
just blindly
invokes its processor you'll be processing two files
simultaneously and have
to worry about the second process affecting any global variables
in use by the
first (obviously this problem can be avoided by not using global
variables in
your processing code). After each of the files is received you'll
invoke a
further layer of processing, until you are processing 5 files at
once, with the
5th file at the outermost layer. It will complete and then file 4
will continue,
and then it finishes and file 3 continues, and so on. You need to
ensure that you
have enough stack and that this really is what you want to do.
With this scheme
you have the sequence:
start 1, start 2, start 3, start 4, start 5, end 5, end 4, end
3, end 2, end 1.
This might be more of a problem if you were to hold limited
resources for each
of the file operations (eg fixed length arrays, limited numbers of
file handles,
etc). Similarly, this may not make sense to the user if you're
reporting that the
last file is being processed, then the penultimate, and so on back
to the first.
This also has implications for the shutdown and error handling -
if there is a
problem in file 5 then the errors need to stop just it (but you
may decide you
want to cancel all the operations, without requiring the user to
click 'cancel'
for every file that's to be processed). If the user wants to quit
the
application, you need to stop every one of those processes from
running, in a
timely manner - if it takes 1 second to stop processing a file,
then it will be
5 seconds before your application quits - is that a problem ?
Implementing a queue of 'files to be processed' is the usual way
to handle this,
adding files as each dataload is received and starting their
processing on the
next null poll when you observe the queue to be non-empty. This
was suggested by
the original poster and would be my preferred solution.
This way the files are processed in order and only a single file
is processed at
a time.
Of course, there is the option to just ignore the DataLoad
operations, or report
an error to the user. Neither of which are particularly friendly.
Remember the system is multitasking. Forcing the user to serialise
their
operations defeats the purpose.
* Iconisation effects should still work.
If you support the iconisation messages, they should be consistent
between the
different ways that the system works.
* Pollwords must be processed - especially the high priority ones -
otherwise
you'll be busy-waiting all the time.
* Does error handling work as you expect?
If you get an error in the poll, will this repeat the next time it
is called
because the state isn't updated as it might be in the 'idle' (non-
processing)
state. If you're nesting operations, is the correct error handler
even called?
If your nested poll call causes an error, it isn't a problem with
the processing
of the file, so shouldn't trigger the error handler for the file
processing.
This is just the same as handling of exceptions in higher level
languages - they
should be processed at the level to which they relate and not left
to propagate
up to higher levels where they will have greater impact.
* Other protocol operations need to be checked.
I can't remember any other system-like operations which might be
present, but the
non-system protocols like External Edit, OLE, Newsbase, etc, need
to be checked
to see that they're doing the right thing if triggered whilst the
application is
in its 'processing' and 'idle' modes.
Most of these you need to check anyhow, but doing so for a separate
subset function that provides polls is an extra burden. Anywhere that
the UI performs differently under some circumstances is a bad design,
and if your application forces that state then the application is
badly designed.
In many cases, restructuring the code such that the processing can
take place in chunks helps. It isn't always the right solution, as in
itself it gives a maintenance (and initial development) cost. You do
gain other advantages in allowing chunked processing, although many
are related to reconsidering what data your are processing,
modularising your processing and creating simpler processing steps.
In essence, doing it properly (allowing multitasking, and ensuring
your application still functions as without problems for the user)
requires that you rethink what you're processing. There are no ways
around that for any non-trivial processing.
Summary:
* Just shoving in a SYS "Wimp_Poll" will work very badly.
* Inserting a special poll handler for certain codes will 'work' but
may give a
very poor experience for the user and requires greater
maintenance.
* Inserting a call to your main poll handler needs consideration for
recursion.
* In all of the above cases, your application and processing code
will need
changes to the UI to indicate available/unavailable operations,
and to handle
the termination in clean manner.
My response to the OP is that their initial feeling is right - set up
a queue of files to be processed, and process them on null polls.
Either process them to completion (which means that your UI needs less
changes, although you may need to modify your exit handler to say that
there is still pending work), or process them in chunks with a call to
your main poll function at intervals (and again you'll need to update
your exit handler to handle the processing of a file being cancelled
part way through). The queueing of files already added will take care
of new files being added to the queue.
However, I would suggest considering splitting up the processing so
that it can handle processing in chunks (if appropriate) as this may
be more flexible in any case. Of course, if the code is written nicely
in a modular fashion then changing the code so that it can be run in a
TaskWindow would merely be a matter of changing the way that it is
called and invoking the processing library differently. It seems
unlikely that this is the case - probably you've got UI code mixed in
with processing logic, which will preclude such an easy change.
--
Justin Fletcher