 The Refresh Daemon Package Architecture and Code Description
 ============================================================

0) Introduction
----------------
This document is intended to help those who want to understand the
architecture and the source code of the refreshd package. Here is a short 
summary: 

o Section 1) shows what the refresh daemon does, 
o Section 2) describes the abstract concept of biditext key 
o Section 3) gives an overview of the refresh daemon operation
o Section 4) gives an overview of the individual components of 
             the refreshd package
o Section 5) presents the code of the refreshd components in some detail.
             It is recommended to read this section in parallel with the code
o Section 6) conclusion and author contact information




1) Problem Statement
--------------------
One of the problems with the current version of biditext library is that after
changing the biditext state (i.e. creating/deleting the .rev file), the
application windows are not automatically refreshed. This means that after
the biditext state has changed, the user has to manually refresh the 
application windows (either by minimizing/restoring the windows 
or by covering/uncovering them with other windows). The goal of the refresh
daemon package is to overcome this problem by automatically refreshing
the biditext enabled application windows whenever the biditext state
changes.

2) The BidiText Keys Model
--------------------------
Each biditext enabled application is associated with a (key, value) pair.
The key can be an arbitrary string, while the value, which is boolean,
indicates the current biditext state (enabled or disabled). In the current
implementation, the key names a file from the file system and the value is
implicitly encoded by the file's existence or absence, but this might
change in future implementations. Several biditext applications can 
share the same biditext key. This means that whenever the value of the
key changes all the windows from the applications that share the same key
have to be refreshed.


3) Overview of the Refresh Daemon Operation
-------------------------------------------
The refresh daemon maintains a database of all the biditext keys and
the application windows which are associated with the biditext keys.
The refresh daemon has two modes of operation: `automatic' and `manual'.
In the automatic mode, a thread periodically wakes up and scans all the 
keys from the database. The thread uses the r2l_query_state() from the r2l
library to check the value of a biditext key. If there
is a change since the last scan, all the windows associated with the 
key are refreshed using the XClearArea() Xlib call. The manual mode of
operation is similar to the automatic mode, except for the fact that
the scan is not performed periodically; instead it is initiated by
receiving an appropriate message (REFRESHD_REFRESH_NOW) from another 
application. This mode of operation is more suitable for GUIs that 
modify the biditext state. Whenever these GUIs modify the biditext 
state, they should send a refresh request message to the refresh daemon 
in order to refresh all the windows of the affected applications.


4) The Components of the Refresh Daemon Package
-----------------------------------------------
The refresh daemon package consists of four components:


1) the refresh daemon hook library
2) the refresh daemon proper
3) the refresh daemon client library
4) the biditext application used to launch biditext-enabled applications

Here is an overview of the components:

4.1) The Refresh Daemon Hook Library (refreshd_hook.so)
-------------------------------------------------------
This is a shared library is preloaded in the address space of the 
biditext enabled application. Its task is to intercept the calls
to XCreateWindow() and XCreateSimpleWindow() Xlib functions, stash the
Display parameter and the return value and pass them to the refresh daemon.

4.2) The Refresh Daemon (refreshd)
----------------------------------
The refresh daemon maintains a database of all the windows of the applications
that have been hooked by refreshd_hook.so. It consists of three threads:
  1) a communication thread which listens on a UNIX domain socket,
     decodes messages,  and performs the appropriate actions
  2) an auto-refresh thread which periodically wakes up, checks if the state
     of any r2l tokens has changed and if so refreshes the appropriate windows.
     This thread does not run if the --no_auto option was passed to refreshd
  3) a scavenger thread which periodically wakes up and checks whether the
     windows from the database are still valid. It does this by trying to
     get the window geometry with XGetGeometry(). If a window is invalid, it
     will be removed from the database.

The daemon understands the following types of messages:

add_window (replies with an ack message)
ping (replies with an ack message)
refresh_now

4.3) The Refresh Daemon Client Library (librefreshd_cnt.a)
----------------------------------------------------------
This library is used in order to hide the underlying communication with the 
daemon. Applications should communicate with the daemon only via this library
as the communication layer might change in future releases.

4.4) The biditext Application
-----------------------------
This application is used to launch a biditext enabled application. This 
application uses r2llib in order to locate the biditext key (.rev file), 
sets the BIDITEXT_FILENAME to the name of the biditext key, and launches 
the application via execvp() by preloading the refreshd_hook.so and 
biditext.so libraries.


5) Detailed Description of the Source Code
------------------------------------------

5.1 The Refresh Daemon Hook Library (refreshd_hook.so)
------------------------------------------------------
There is one source file refreshd_hook.c under src/refreshd_hook.
The code here is not particularly interesting. It provides its 
own versions of XCreateWindow() and XCreateSimpleWindow(). 
The code of these two functions is very similar. They call the old 
versions of the functions and pass the window ID to the refresh daemon.
The library is automatically initialized through the _init() function.
The loader calls this function whenever the library is initialized.



5.2 The Refresh Daemon (refreshd)
---------------------------------

5.2.1 The Window Database (class WindowDB)
------------------------------------------

The central part of the refresh daemon is its window database. This component
is coded in the following source files under src/refreshd:

o window_db.h/window_db.cc and
o window_db_iterator.h/window_db_iterator.cc

The window database (class WindowDB) has a hierarchical tree-like structure
with three levels:


       Level 1               Level 2       Level 3
       (biditext keys)      (displays)     (windows)

---+---biditext_key1------+---dpy1----+----wnd1
   |                      |           |
   |                      |           +----wnd2
   |                      |           |
   |                      |           +----wnd2
   |                      |
   |                      +---dpy2----+----wnd3
   |                                  |
   |                                  +----wnd4
   |                        
   |                       
   +---biditext_key2-----+---dpy3-----+----wnd5
   |                     |            |
   |                     .            +----wnd6
   |                     .            |
   .                                  .
   .                                  .



The topmost level contains the biditext keys. The following data are stored
together with the keys: an r2l token (of type r2llib_t) and the state of the 
r2llib_t token during the last scan (stored as a r2lstate structure). All this
data is stored inside the PerBidiTextKeyData structure. If during a refresh 
traversal a difference between the current state of the r2l token and the
state stored during the previous scan is detected, the windows associated with
this key (i.e. all the leaf nodes) are refreshed.

The intermediate level consists of the displays which the application
windows are associated with. A Window ID can be used only in conjunction
with an X display connection. refreshd tries to minimize the actual connections
to the X server, by attempting to share X connections to the same display.
See the description of the AutoDisplay class below.

This is more or less the data structure used. Next I am going to describe
the operations on this data structure.

add() as you probably guessed adds items to this data structure. 

In order to traverse the window database, several iterators are used.
A WindowDBBidiTextKeyIterator iterates over all the biditext keys  (level 1)
from the database. This iterator provides the following operations: 
o getBidiTextKey() -- returns the biditext key
o getLastState() -- returns the value of the biditext key before the
                   current scan
o getCurrentState() -- returns the current value of the biditext key.
o setLastState() -- set the value of the last biditext key
o operator ==, !=, ++ (both prefix and postfix) with their usual meanings.

The bidiTextKeyBegin() function from WindowDB returns an iterator to the first 
biditext key while bidiTextKeyEnd() returns an iterator which is 'one beyond
the last' key.

The DisplayMapIterator iterates over all the displays that are controlled
by the same biditext key (r2l token serialization in the current 
implementation). The current version of refreshd supports applications 
that might use several X connections (displays) at the same time 
which can be all controlled by the same biditext key. This is an STL map 
iterator. This means that its unary * operator will return a 
pair<AutoDisplay*,WindowSet>. If dpmIt is a DisplayMapIterator 
(*dpmIt).first will return the pointer to AutoDisplay (AutoDisplay is 
described later), and (*dpmit).second will return the set of windows 
associated with that display. This iterator supports the usual ++, ==, 
!= operators.

WindowDB::dpyMapBegin() returns an iterator to the first display controlled by
a biditext key. The biditext key is passed indirectly through the
BidiTextKeyIterator& parameter. dpyMapEnd() returns an iterator `one beyond
the last' display controlled by the key. 

The WindowSetIterator is an iterator on all the windows controlled by a 
display. It is an STL set iterator. Its unary * operator returns a window ID.
It supports the usual ++, == and != operators.

WindowDB::wndsBegin() returns an iterator to the first window controlled
by a display. The display is passed indirectly through the 
DisplayMapIterator& parameter. WindowDB::wndsEnd() returns an iterator to 'one
beyond the last window' controlled by the display.

WindowDB has also a member function for removing elements. It is 
used by the refresh thread and the scavenger thread. What!? the refresh 
thread is removing elements from the database!? Yes, if the window
IDs are no longer valid. 

The database is accessed and modified by the communication, refresh.
and scavenger threads. In order to prevent corruption, the lock() and
unlock() member function provide mutual exclusion. Whenever a thread tries to
access the database, it acquires its lock first by calling lock(). When
the thread is done accessing the database, it releases it by calling unlock().



5.2.2 The AutoDisplay class
----------------------------
The AutoDisplay class (defined in auto_display.h and auto_display.cc) is a 
wrapper around an X Display. However, it tries to avoid using multiple 
connections to the same display. The only way to get an AutoDisplay
object is by calling the getInstance() static method. This function checks if 
there is not already an open X connection on that display and if it is, 
it returns a pointer to an already created AutoDisplay object (and 
increments that object's reference count). The only way to destroy an 
AutoDisplay object is by calling its release() method. This decrements 
the object's reference count. If the reference count reaches zero, the X 
connection is also closed.

5.2.3 The Refresh Daemon's Threads
----------------------------------

5.2.3.1 The Communication Thread
--------------------------------
The communication  thread (communicator() from communication.cc) handles all 
the communication  with the external world. The communication takes place 
over UNIX domain datagram sockets. The communication thread waits for 
messages  and processes them. The name of the socket is /tmp/refreshd_svc
refreshd tries to use abstract socket names (i.e. socket names not bound to 
the file system). To do so, #define the ABSTRACT_UNIX_SOCKET_ADDRESSES
in the refreshd_params.h file. This is supported only by Linux kernel
versions larger than 2.2.0 On earlier versions and on other UNIX
flavors, you should not define the macro.
The rest of code is not particularly interesting. Just a switch statement 
to dispatch the various types of messages.  

Application programs should not send messages directly to the daemon over 
sockets. They should use the librefreshd_cnt.a library, described further on. 
The communication layer  might change in future releases of refreshd. 
The REFRESHD_ADD_WINDOW  message is synchronous (i.e. an acknowledgement 
is sent upon its receipt). This is done in order to prevent some race 
conditions which might cause the refresh daemon to miss refreshing window 
if its corresponding biditext key value (r2l token state) has changed shortly 
after the window has been created. The REFRESHD_PING message is also 
synchronous for obvious reasons.


5.2.3.2 Traversing the Window Database
--------------------------------------
In the current implementation of the refreshd, the refresher and scavenger
threads perform almost the same task. Both have to traverse the database.
This common behavior is captured inside the traverse() function from
traverse.cc The function accepts three pointers to callback functions.
The function iterates over the window database tree in a DFS order. 
For each node of the tree the appropriate callback is invoked, depending
on the node's level. Depending on the value returned by the callback,
the iteration continues at the same level (WDB_IT_CONTINUE), goes
on to a level above (WDB_IT_BREAK) or goes on to one level below
(WDB_IT_NOTHING). The auto_refresh and scavenger threads call this 
function with appropriate callbacks. The traverse() function assumes
that third level callbacks might cause X errors. Therefore an appropriate
X error handler has to be set up that puts the offending window IDs inside
a `bad List'. After a complete third level iteration, XSync() is called
and all the windows from the `bad list' are removed from the database.


5.2.3.3 The Refresh Thread
--------------------------
The refresh thread (refresher() from refresher.cc) performs the actual 
refreshing of the windows. This code is actually executed from the 
refreshd application's main thread (i.e. after the application initializes
and creates all the other threads, it enters the refresh infinite loop).
The refresh thread waits on a conditional variable to be signaled. 
Two threads can signal that variable. One is the auto-refresh
thread when it wakes up and the other is the communication thread upon
receiving a REFRESHD_REFRESH_NOW message. The auto-refresh thread 
(autoRefresher() from refresher.cc) is not created if the --no_auto command 
line option has been passed to refreshd. In that case, the only way to 
initiate a refresh is to send a REFRESHD_REFRESH_NOW message to the daemon 
(e.g. from a GUI). The refresh threads calls traverse with the following 
callbacks:

1) The first level callback checks if there is a change in the state of
   the biditext key value (r2l token). If there is no change, the callback 
   returns WDB_IT_CONTINUE (i.e. go to the next biditext key, the iteration
   should not continue on a lower level). Otherwise (i.e. there is a change)
   the callback returns WDB_IT_NOTHING.
2) The second level callback is not used, so a NULL is passed.
3) The third level callback calls XClearArea(dpy,w,0,0,0,0,True); in
   order to perform the actual refresh. The call might fail, in which case
   the window has to be removed from the database, as described at the end
   of the previous section. This function always returns WDB_IT_NOTHING.



5.2.3.4 The Scavenger Thread
----------------------------
Consider the following scenario: A biditext enabled process runs for a while
and then it dies unexpectedly. All its windows are in the refreshd database.
Question: When are these windows going to be removed from the database?
Answer: upon the next refresh, because then the windows will not be valid
and the refresh thread will remove them. However when is this refresh going
to happen? Well, maybe never. If nobody changes the value of the biditext key
(state of the r2l token) and nobody sends a REFRESHD_REFRESH_NOW message
to the refresh daemon, no refresh will ever happen and the windows that
belonged to the dead process will remain in the database indefinitely. Here
is where the scavenger comes into play. The scavenger thread periodically 
wakes up and checks if the windows from the database are still valid.
It does this by invoking traverse() with the following callbacks:

1) The first level callback is not used, so a NULL is passed
2) The second level callback is not used either, so a NULL is also passed
   for it.
3) The third level callback attempts to get the geometry of the window by
   calling XGetGeometry() for it. The call might fail, in which case
   the window has to be removed from the database, as described at the end
   of the "Traversing the Window Database" section above. This function 
   always returns WDB_IT_NOTHING.



5.2.3.5 main() (main.cc)
------------------------
Nothing particularly interesting here, though certain things might be worth 
noting. After the command line arguments are parsed, reefreshd forks(). The
parent process sets up a signal handler that forwards a TERM or INT signal
to its child and then waits() for the child process to exit. If the child
process has exited, the parent process take care to unlink the server
socket from the file system, if file system bound socket names are used.
After that the parents exits.

The child process initializes the multi-threading support of Xlib,
sets up an X error handler, and creates the threads. autoRefresh is not 
created if --no_auto has been passed.


5.2.3.6 debugging support (debug.c)
-----------------------------------
refreshd uses syslog to print various debug information. There are three
levels of severity, errors which indicate fatal conditions (such as
bugs inside the code) , warnings which indicate abnormal conditions 
which should not cause the daemon to crash (such as receiving an 
invalid message) and informational messages. Printing the messages
is handled by the ERROR(), WARNING() and MESSAGE() macros. The 
amount of debug information is controlled by the NOISY and DEBUG_NOISE_LEVEL
macros (see debug.h) NOSY is defined in the compilation command line,
while DEBUG_NOISE_LEVEL should be defined in debug.h If NOISY is 
not defined, no debug information is written at all. Otherwise
the amount of information is controlled by DEBUG_NOISE_LEVEL.


5.3 The Refresh Daemon Client Library
-------------------------------------
This library is used by all the applications to communicate with the refresh
daemon.  This library hides the underlying communication layer with the daemon.
The refreshd_hook.so library is linked with this library and uses it to 
communicate with the refresh daemon. It also takes care to wait for the
acknowledgements from the daemon if it has to (i.e. REFRESHD_ADD_WINDOW) 
messages. 

A GUI can also link with this library in order to be able to initiate 
refreshes calling rdCntRefreshNow().

In order to ensure full multi-threaded support a socket is created for each
message sent. The socket is destroyed after the message has been sent and
the acknowledgement has been received (if it had to be received). I think
that doing otherwise cannot avoid race conditions caused by multiple
threads invoking the same function and/or socket descriptors being duplicated 
by fork(). In order to be able to send a message over a UNIX domain socket,
the socket has to be bound to a name. I chose a naming scheme that attempts to
minimize collisions. The name is created from the current process id,
a time stamp obtained via gettimeofday() and a random number.
 
In order to transmit biditext keys over sockets I use Xlib atoms, in order
to avoid imposing an upper bound on the biditext key lengths. Because of this
an application linking with refreshd_cnt must also link with Xlib (-lX11).


5.4 The biditext Application
----------------------------
The biditext application supersedes the biditext shell script from the
original biditext distribution. It performs all the tasks that shell script
used to do (i.e. setting the LD_PRELOAD variable and executing the 
application).
In addition to those tasks, biditext does the following:
It creates an r2l token using r2l_init() and then it sets the BIDITEXT_FILE
environment variable to the serialized version of the r2l token obtained
via r2l_get_text_serialization().

The code adds the refreshd_hook.so and biditext.so libraries into
the LD_PRELOAD environment variable and after that execvp()s the application.


6. Conclusion
-------------
Version 0.1.0 is a major rewrite of version 0.0.2. While this version
does not bring any significantly new features, it has several internal 
improvements concerning multi-threading, error handling, and integration
with r2llib. Despite my best efforts to avoid them, it is likely that some
bugs have crept in. Therefore your feedback is very important to me.
Feel free to send any comments, suggestions, corrections, feedback 
or flames regarding both refreshd or this document to me 
(e-mail: emild@cs.technion.ac.il). 

