IIIM server code reading note (part 1)
+----------+
| <<if>> |---o request_accept ()
| IMAccept |---o request_connect ()
+----------+ +--------+
A +---------|>| IMAuth |
| | +--------+
| |
+-------+ +-----------+ +--------+
| IMSvr |<>----+----| IMUserMgr |<>-----| IMUser |
+-------+ | +-----------+ 1 * +--------+
| |
. | +-------+ +--------+ +--------+
| +----| LEMgr |<>-----| LEBase |-.-.-.->| libiml |
. | +-------+ 1 * +--------+ +--------+
| |
. | +------------+ +---------+ +---------+
| +----| IMProtocol |<>-----| IMState |<>-----| ICState |
V +------------+ 1 * +---------+ 1 * +---------+
+-------------+ A A
|<<singleton>>|---o start () | |
| IMScheduler |---o stop () | +-------------------------+
+-------------+ +--+ |
A | |
| | |
+------------------+ +--------------+ +----------------+ |
| IMScheduler_MTPC |-.-.-.-.->| IIIMProtocol |<>-----| IMSocketListen | |
+------------------+ +--------------+ 1 1 +----------------+ |
\ <> |
\ | |
+-------------------------------+ +----------map {IIMPTrans*, IIIMP_IMState}
/ Make a thread per connection. |
| Invoke thread func IMScheduler_ |
| MTPC_thread_entry () |
+---------------------------------+
+-------+
+-.-.-.-.->| LEMgr |
| +-------+
+-----------+ |
| <<if> | +--------------+ +--------+
| IMHandler |<|-----| IMConnection |----->| IMUser |
+-----------+ +--------------+ 1 1 +--------+
|1 |1 |1 | 1
| | | |
| | | V *
| | | +-----------+
| | +-------->| IMDesktop |
| | 1 +-----------+
| |
| | +-------+
| +-------------| IMSvr |
<<create>> | 1 +-------+
+--------------------+
|
| +---------+ +-----------------+
| | ICState |<>-----| IMLExec_ICState |
| +---------+ 1 1 +-----------------+
| |
| . <<use>>
| |
V * V
+---------------+
| <<if>> |----o send_event ()
| ICHandler |----o ...
+---------------+
A
|
+----------------+ +--------------+
| IMInputContext |<----->| IMConnection |
+----------------+ * 1 +--------------+
| | 1
. | +-----------+ +-----------------------+
| +-------------->| LEContext |-------/ One ic may corresponds |
. * +-----------+ | to several LE context |
| +-------------------------+
. +-------+
+-.-.-.-.->| LEMgr | Note: LEMgr should be a singleton?
. +-------+
|
. +---------+
| | <<if>> | +-----------------+
+-.-.-.-.->| IMLExec |<|-----| IMLExec_ICState |
+---------+ +-----------------+
+------------+ +---------------+ +------------------+
| IIIMPTrans |<>-----| IMSocketTrans |<|-----| IMSocketTransTLS |
+------------+ 1 1 +---------------+ +------------------+
* Before connected:
1. IIIMProtocol::accept ()
IIIMProtocol::restart (), initialize the private member
IMSocketListen *pimsl.
use pimsl to accetp a client socket, and return an IMSocketTrans
instance, if passes the authorization, create an IIIMP_IMState
object and return.
2. IMScheduler_MTPC::start ()
create a pthread for this client's connection
3. IIIMProtocol::receive_and_dispatch ()
pthread loop IMScheduler_MTPC_thread_entry call this function to
handle client requests.
IIIMPTrans::receive () to receive an IIIMP_message, then call 4.
4. IIIMP_IMState::dispatch ()
if ic_id is invalid, call IMState::deliver (). Since the connection
has not been established, the IMState::pproc_state should be null,
so it then call IIIMP_IMState::message_proc ().
5. IIIMP_IMState::message_proc()
in case of IM_CONNECT message, get userinfo from request, call
IMSvr::request_connect (). This IMAccetp interface tries to
authorize this user, if successed, create an IMConnection and
returned as IMHandler*. Change imstate to IIIMP_IMState_Identified.
6. IIIMP_IMState_Identified::message_proc ()
If connected clients request an operation for im instead of ic,
(e.g., IM_CREATEIC, IM_DISCONNECT etc), this method will be
invoked, while in this case the ic_id must be invalid.
in case of IM_CREATEIC, call IMConnection::createic () to create an
IMInputContext instance and returned as ICHandler*. call
IIIMP_ICState::create(ic_id, this, pich) to create an icstate
instance, add it to icmap, then send a reply to client.
* After connected:
1. IIIMProtocol::receive_and_dispatch ()
2. IMState::dispatch ()
if ic_id is valid, from the IMState::IMShared::ICStateMap, look up
the matched ICState, then call ICState::deliver () to handle the
client's request or reply.
3. ICState::deliver ()
get current ICState, and then call its message_proc ().
IIIMP_ICState::start_request() will turn the current state to
IIIMP_ICState_REQUESTED. While IIIMP_ICState_REQUESTED::finish ()
will restore to previous state, and destory itself.
if current state is requested, its message_proc () will return
false, since it can not handle any request at that time.
if current state is waiting, its message_proc () will see whether
the incoming message is what it's waiting for. if not, it calls
IIIMP_ICState::message_proc () to handle this message, so in
waiting state will not block the normal request. or, it retrieves
and restore to the previous back-up request state, and then call its
dealing method. this method in trun calls get_imlexec()->execute(),
this method execute all pending iml instructions.
IIIMP_ICState_REQUESTED::wait ()
IIIMP_ICState_REQUESTED::wait_aux ()
These two methods can change the current icstate from "requested"
to "waiting" (for the replies from client).
IIIMP_ICState::message_proc()
in case of IM_SETICFOCUS:
start_request()->toggle_icfocus () [REQUESTED::toggle_focus ()]
get_ichandler()->toggle_focus () [IMInputContext::toggle_focus ()]
get_current_lecontext()->toggle_focus () [LEContext::toggle_focus ()]
if_SetSCFocus () [SunIM.c: if_SetSCFocus ()]
s->If->ifm->if_SetSCFocus () [specified LE's setfocus function]
dealing ()
execute remaining IML instructions (pushed by LEs)
finish (), restore to previous state
* How does LE invoke server?
LE can not directly call server functions to send reply/messages to
client. LE calls iml methods (defined at iiimsf/lib/iml/SunIMSubr.c)
to create iml instructions, and append them to the operation list
(via iml_execute(), virtually is iml_execute_iml_wrapper() in LE.cpp).
As we mentioned early, IIIMP_ICState_REQUESTED::dealing() calls
IMLExec_ICState::execute() to process the pending instructions.
IMLExec_ICState::execute_opcode(), in turn calls imli_xxx methods,
these methods then call IIIMP_ICState_REQUESTED member functions which
actually send messages to client.
iml_make_commit_inst()/iml_execute()
|
| through an async flow
V
in IMLExec_ICState::execute_opcode()
case IMM_COMMIT:
IMLExec_ICState::imli_commit ()
IIIMP_ICState_REQUESTED::commit_string ()
new an iiimp message
send ()
IIIMP_ICState::send ()
IIIMP_IMState::send ()
IIIMPTrans::send ()
iiimf_stream_send ()
IIIMPTrans_write ()
IMSocketTrans::send () or
IMSocketTransTLS::send ()
* NameSpace I/O functions:
NameSpace I/O functions are passively invoked by LEs.
in each nsio function, there is a nested event dispatching loop.
Take open_ns () as an example:
send_message = iiimp_open_ns_new (...);
xims->send (send_message, true);
for (;;) {
xims->get_iiimptrans ()->receive ();
if (opcode == IM_OPEN_NS_REPLY) {
break;
} else {
xims->dispatch (...);
}
}
this loop is very simliar with IIIMProtocol::receive_and_dispatch.
* Misc:
ICState::send_avail_p ()
IIIMP_ICState::state_send_available_p () { return false; }
IIIMP_ICState_REQUESTED::state_send_available_p () { return true; }
IIIMP_ICState_WAITING::state_send_available_p () { return false; }
-----------ICState::deliver ()--------------
if (!p->message_proc(message)) return false;
if (send_avail_p() && get_imlexec()) {
result = get_imlexec()->execute();
}
--------------------------------------------
only when the icstate in requested, ICState::deliver () will try to
execute the buffered iml instructions. Because only in this state, the
server side (LEs) need to respond to client, and then LE may push iml
instructions in the queue.
Basicly, every branch in IIIMP_ICState::message_proc () will call
IIIMP_ICState_REQUESTED::dealing (), which actually calls
get_imlexec()->execute(). Is it a dup invoke in ICState::deliver ()?

