• 沒有找到結果。

Chapter 2 Home Audio/Video Interoperability

2.5 Java and HAVi

2.5.5 An Example

}

public class RegistryLocalClient extends RegistryClient { RegistryLocalClient(SoftwareElement se);

}

2.5.5 An Example

In Example 2-5, a software element will be created to represent this application, which offers a “Hello World” Service.

In line 16, a software element is initiated in the constructor and ”

this

”, a HaviListener, is passed as a parameter. In lines 31,

receiveMsg()

handles incoming messages to the software element. The HAVi message payload contains three fields:

operation code, control flag, and transaction identifier. These fields are retrieved in lines 42-44. Operation code specifies the service being requested. Control flags indicate whether the incoming HAVi message is a request or response. Transaction identifier is an integer allowing the destination software element to match response with requests. In lines 49, it does a switch on the operation code of the requested service. If it is a “Hello World Service” request, a “Hello World” string will be printed and a response will be sent via the

msgSendResponse()

API of the software element (

mySe).

Example 2-5: A Simple HAVi Java application

1: package lab441.havi.demo.thesis;

2: import ntu.havi.constants.*;

3: import ntu.havi.system.*;

4: import ntu.havi.types.*;

5: ####

6: public class SimpleApplication extends HaviListener {

22 7: ####private SoftwareElement mySe = null;

8: ####// operation code for “Hello World Service”

9: ####private final byte HelloWorldId = (byte)0x80;

10: ####private OperationCode opCode = null;

11: ####private byte controlFlags = 0;

12: ####private int transactionId = 0;

13: ####private Status returnCode = null;

14: ####

15: ####public SimpleApplication() throws Exception { 16: ########mySe = new SoftwareElement(this);

17: ########// register this application 18: ########RegistryLocalClient registry = new

19: #################################### RegistryLocalClient(mySe);

20: ########Attribute[] att = new Attribute[0];

21: ########HaviByteArrayOutputStream hbaos = new 22: ################HaviByteArrayOutputStream();

23: ########hbaos.reset();

24: ########hbaos.writeHaviString("Hello World");

25: ########att[0] = new Attribute(

26: ################ConstAttributeName.ATT_DEVICE_MANUF, hbaos);

27: ########registry.registerElementSync(3000, mySe.getSeid(), 28: #################################### att);

29: ####}

30: ####

31: ####public boolean receiveMsg(

32: ############boolean haveReplied, 33: ############byte protocolType, 34: ############SEID sourceId, 35: ############SEID destId, 36: ############Status state,

37: ############HaviByteArrayInputStream payload) { 38: ########if(haveReplied)

39: ############return false; // another listener has replied 40: #######// retrieve operation code from the HAVi message header 41: ########try {

42: ############opCode = new OperationCode(payload);

43: ############controlFlags = payload.readByte();

44: ############transactionId = payload.readInt();

23

45: ############if((controlFlags & 0x01) == 1) {

46: ################// incoming message is a response, not a resquest 47: ################return false; // ignore

48: ############}

49: ############switch(opCode.getOperationId()) { 50: ################case HelloWorldId:

51: ####################System.out.println("Hello World");

52: ####################returnCode = new Status(opCode.getApiCode(), 53: ############################ConstGeneralErrorCode.SUCCESS);

54: ####################mySe.msgSendResponse(sourceId, //?

55: ############################opCode,

56: ############################ConstTransferMode.SIMPLE, 57: ############################returnCode,

58: ############################null,

59: ############################transactionId);

60: ################return true;

61: ############default: return false; // unknown operation id 62: ############}

63: ########} catch(Exception e) { 64: ############e.printStackTrace();

65: ########}

66: ####} // end of receiveMsg()

67: } // end of class MyHAViApplication

24

Chapter 3

Design Methodology of Software Element

3.1 Pre-assumption of Messaging System

It is critical that software elements carefully use the service of Messaging System and Messaging System efficiently dispatches messages to software elements. The design of software element is strongly related to that of Messaging System (MS), especially how MS dispatch messages and invoke callback functions.

According to HAVi specification, while a software element is performing its callback function, it does not block other software elements or the underlying Messaging System. Besides, during the time that the callback function blocks, the software element may not be able to process other incoming messages

3.2 Design Issues

The basis of a HAVi network is that requests are sent from software element to software element, actions are taken, and corresponding responses are returned. How a software element handles these requests, actions, and responses is largely up to the developers. The design of software elements strongly affects the efficiency of HAVi system.

We encountered some design issues about software elements while we designed and implemented our HAVi stack. We concluded them into four issues.

25

Software Messaging Messaging Software

Element A System 1 System 2 Element B

msgSendRequest(B) Invocation

msg_reliable(B)

Callback(req) Invocation

Callback(req) Return msg_reliable_ack(A)

msgSendRequest(B) Return: OK

3.2.1 Blocking Time

When a software element sends an asynchronous request, it will block until the acknowledgement returns or a timeout occurs. The software element that receives a callback invocation has to immediately return from the callback. However, the callback function may take long to complete, and such a design may result in poor performance. For example, when a Registry gets a query, the Registry has to forward the query to every other Registry in the network and collects the responses. It may take much time, and the software element that sends the query may be blocked for a long time.

Figure 3-1: Blocking time

3.2.2 Multiple Requests

When a software element receives a message, it will trigger various actions. If the actions are implemented in a way that blocks the software element that receives new messages, no new message will be processed until the previous action completes. In the case that a software element receives multiple requests at the same time, many of them may end with timeouts. For example, when a Registry gets a query, it has to forward a query to every other Registry in the network. Suppose that a Registry gets several queries at the same time and it handle the queries one by one. While the first query is being processed, all other queries will be queued. It is inefficient and may cause many timeouts.

Blocking Time

26

Figure 3-2: A Registry forwards the query to every other Registry

3.2.3 Synchronization

Suppose that a software element is implemented in a way that it handles one request at a time. If a callback function includes a request to another software element, a deadlock may occur. For example, when two Registries simultaneously query each other, but cannot receive the responses, a deadlock may occur because they cannot complete their actions until the queries from other Registries are answered.

For example, in the case that a Registry is implemented in such a way that it uses synchronous request within its callback function to forward GetElement() requests to other registries. When two applications on two different devices, A1 and A2, happen to query their Registries, R1 and R2, at approximately the same time, a deadlock may occur. R1 is busy handling the request of A1 and waits for the response to the GetElement() forwarded to R2. During this period, R1 cannot process incoming messages. Furthermore, R2 is busy handling the request of A2 and waits for the response to the GetElement() forwarded to R1. Also, during this period, R2 cannot process incoming messages. This deadlock situation will result in timeouts of the synchronous call in both R1 and R2.

IEEE 1394

27

Figure 3-3: Two Registries simultaneously query each other

Suppose that a software element can handle multiple requests at the same time. The software element is handling two requests simultaneously. If the two requests may change the same data in the software element, the data should be protected by synchronization mechanism. For example, a Registry is processing two requests, RegisterElement() and UnregiserElement(), at the same time. Since RegisterElement() will add an entry to the Registry database and UnregisterElemen() will delete an entry, an error may occur if there is no synchronization mechanism.

There is a more complicated situation. When a software element receives a new request, it should stop the former requests and process only the new request. For instance, a network reset event is typically generated when the network topology changes or a device is activated or deactivated. Whenever a DCM Manager receives a network reset event, it should start the leader selection protocol, which may take a long time to complete. If the DCM Manager receives another network reset event, the leader selection protocol should be restarted, that is, the new callback thread should notify the thread executing protocol to stop.

3.2.4 Multicasting

Multicasting is a delivery of information to multiple destinations simultaneously. In this report, multicasting refers to sending HAVi messages to multiple software elements simultaneously.

28

A software element may have to send messages to more than one software elements and wait for responses. The number of destination software elements could be large.

For example, GetElement() method of Registry is used to get a list of software element identifiers that satisfy the query given through the parameter. When a Registry receives a query from a local software element, the Registry has to forward the query to all other Registries in the network and collect the responses. In the Figure 3-4, it shows that a Registry receives a request from a software element, and then forwards the query to all other Registries in the network.

Figure 3-4: A Registry forwards the query to all other Registries

It is common for a software element to send messages to various software elements at the same time. It seems easy, but if we study this problem in depth, every solution brings more problems.

IEEE 1394 Messaging

System

Messaging System

Messaging System

Device A Device B Device C

Registry SE

Registry Registry

SE SE

SE

query

query query

29

Table 3-1: Situation when a software element sends multiple HAVi messages

Software Element API Description

DMinitilization DMInquiry DMCommand

Query other DCM Managers DCM Manager

DMInitialInquiry Notify other DCM Managers Event Manager(EM) PostEvent

ForwardEvent

Forward event to other EMs

Registry GetElement

MultipleGetElement

Query other Registries

StreamManager GetGlobalConnectionMap Query other StreamManagers

Reserve Reserve a number of FCMs.

ScheduleAction Bandwidth checking protocol ResourceManager

GetScheduledConnections Get a list of connections Any application

The simplest solution is that the software element sends the synchronous messages in turn. At first, the SE synchronously sends the message to the first target, and the software element will block until it receives the response. Then, it sends the message to next destination SE. After the software element send the messages to all destination SEs, it finishes. Apparently, this method takes too much time and it is inefficient.

Another solution is to use additional threads. The software element creates several worker threads to send synchronous requests. The term “worker threads” here refers to the threads created to help the original thread to do some jobs. Every worker thread sends a message to a destination software element. However, this may not work because if the SE waits the response in the callback function, the SE may not be able to process other incoming messages. Also, a small number of threads are acceptable for simple situations, but multiple threads may be costly, and it may be difficult to predict the maximum number of threads needed. How the worker threads communicate with each other is another problem.

The third solution is to send requests asynchronously. The software element sends asynchronous messages to different destination software elements in turn. The SE does not block while sending. Instead, the callback function will be invoked automatically. Note that the software element shall not wait responses in the callback function since it may not handle other messages. When making an asynchronous

30

request, a software element must store the transaction ID and possible other information about the original request in a sort of table. Normally the entries are removed every time a matching response is received. In the case that a matching response is not received, the corresponding request information will not be automatically removed from the table, causing a potential memory leak. Worse yet, if the SE never receives the response, it may not be able to complete its action, causing parts of the HAVi system to freeze. There are many reasons that may cause responses not to be received. One reason may be that the destination software element, or destination device, is removed during a request. In these cases a timeout will occur, completing the request.

To avoid these problems, software elements that implement asynchronous requests should also implement a timeout mechanism. The timeout mechanism would ensure that actions are completed, and that request data does not build up in tables.

Unfortunately, the timeout may not occur until after a long delay, resulting in very long response times.

Overall, software element architecture should satisfy the requirements in the above issues and has an efficient and simple design.

3.3 Design for Blocking Time

A calling software element sending asynchronous message is blocked until the callback function of the target software element returns. To avoid blocking, the callback function should create a thread to handle the message and then return as soon as possible.

Example 3-1: Creating a thread to handle messages in the callback function

1: class Application extends HaviListener { 2: ####MessageHandler msgHandler = null;

3: ####public boolean receiveMsg(boolean haveReplied, 4: ############################byte protocolType, 5: ############################SEID sourceId, 6: ############################SEID destId, 7: ############################Status state,

31

8: ############################HaviByteArrayInputStream payload) { 9: #######// create another thread to handle the message

10: ########// pass the parameters to the message handler 11: ########msgHandler = new MessageHandler(haveReplied,

12: ######## ##########protocolType, sourceId, destId, state, payload);

13: ########msgHandler.start(); // start the thread

14: ########return true; // the callback thread can return quickly 15: ####}

16: }

17: class MessageHandler extends Thread { 18: ####void run() {

19: ########// handle the message 20: ####}

21: }

In order to reduce the blocking time more, a software element should activate a watch on the target before sending a request. Messaging System provides an msgWatchOn() API for a software element to be notified if the target is removed. Another option would be to register for GoneDevices and GoneSoftwareElement events. When being notified the target disappears, a software element can respond more quickly.

3.4 Design for Multiple Requests

In order to handle multiple requests at a time, multiple threads are necessary.

Adopting the solution in 3.3 , software elements can handle multiple requests at the same time. However, initiating a new thread every time is time-consuming. If request for the software element are time-critical, the software elements could implement thread pooling to reduce response time.

Example 3-2: A software element with three threads

1: class Application extends HaviListener { 2: ####// this software element has three threads 3: ####Worker[] workers = new Worker[3];

4: ####public boolean receiveMsg(...) {

32

5: #### ###if (any of the three threads is available) {

6: ############// resume the available thread to handle the message 7: ########}

8: ########else { // new another thread to do the job 9: #### #### ##msgHandler = new Worker(...);

10: ############msgHandler.start();

11: ########}

12: ########return true;

13: ####}

14: }

15: class Worker extends Thread { 16: ####...

17: }

18:

This software element owns three threads. In line 5, if any of the three threads is available, just resume it to process the message. Or else another thread is created to handle the message because the callback function should return as soon as possible (line 9).

3.5 Design for Synchronization

Adopting two solutions above, software elements can process multiple requests at the same time. The deadlock condition mentioned in 3.2.3 is unlikely to happen since the callback function always returns in a short time. However, different worker threads may manipulate variables of a software element simultaneously, which may introduce errors. To avoid this, we can use Java synchronized data structure such as Vector and Hashtable [16], or use Java build-in synchronization mechanism (synchronized method modifier and synchronized statement block).

There is another issue: while a worker thread is processing a request, a new incoming request may have to interrupt it. Java built-in language synchronization can be used in this situation. When a Thread object’s interrupt() method is invoked, a

InterruptedException

will be thrown. If a worker thread can be carefully implemented, it can be interrupted just by invoking its interrupt() method.

33

Example 3-3: An example of interrupting a thread

class Application extends HaviListener { MessageHandler wt;

public boolean receiveMsg(...) { if(under necessary condition) {

wt.interrupt(); // interrupt the previous thread }

return true;

} }

class MessageHandler extends Thread { void run() {

try {

// process the incoming message

if(Thread.interrupted()) // check if interrupted throw new InterruptedException();

// continue to process the message } catch(InterruptedException e) { return;

} } }

3.6 Design for Multicasting

In this section, a model is proposed to send messages to multiple software elements efficiently. This model may be implemented in synchronous or asynchronous messaging. Besides, an API,

msgSendMultipleRequest()

, is added to the

SoftwareElement

class for developers.

34

3.6.1 Synchronous Multicasting

In the synchronous message mode, a caller will block until a response is received.

Since sending synchronous messages in turn is not efficient, the only way is to use additional worker threads to send synchronous requests.

In the beginning, the callback thread creates another thread to handle the message so that the callback thread can return immediately. Then, the message handler thread creates worker threads to send messages: one thread for one target software element.

While the worker threads are sending synchronous messages, the message handler thread checks if all responses are returned unless a timeout condition occur.

Example 3-4: The design in synchronous multicasting

public class Application extends HaviListener { public void multicasting(...) {

for(int i = 0; i < num_of_target; i++) {

new WorkerThread(ith request, ...).start();

}

while(not timeout && not all acknowledgement received) { sleep(1000);

} }

class Worker extends Thread { void run() {

// send the request

mySe.msgSendRequestSync(...);

} } }

However, multiple threads could carry lots of resource overhead. The number of destination software elements is unpredictable; it could be just one or a large number

35

like 50. Each thread requires memory resources and processor resources. Besides, there is also work involved in starting a thread. Thread pooling can be considered.

3.6.2 Asynchronous Multicasting

In this design, only one thread is required. At first, the message handler thread sends asynchronous messages to all targets in turn. New entries, including the transaction ID and other information about the request, are stored in a table. The thread waits all the responses return until a timeout occurs. Even if no responses return (e.g. a network failure), the thread can continue the work, avoiding the problem that the part of the software elements freezes. When the thread is waiting, the callback function may receive responses and store them in the table. The thread stops waiting if all the responses return or a timeout occurs, and then the entries in the table are removed, so there will be no memory leak.

Example 3-5: Using only one thread to send messages to all target software elements

class Application extends HaviListener { Hashtable table;

public receiveMsg(...) {

// store the responses in the table }

void multicasting(...) {

for(int i = 0; i < num_of_target; i++) { mySe.msgSendRequest(...);

}

while(not timeout && not all responses have received) { // keep waiting

} } }

36

3.6.3 Comparison

In network programming, I/O can be classified as synchronous I/O or asynchronous I/O. “

msgSendRequestSync()

” acts as synchronous network I/O; the caller blocks until the response returns. “

msgSendRequest()

” acts as asynchronous network I/O;

the caller does not block and wait for the response. In network programming, asynchronous network I/O is generally more efficient than synchronous network I/O.

However, for the multicasting issue in HAVi, we think the synchronous design is better for two reasons.

First, the synchronous design in 3.6.1 is faster than the asynchronous design in 3.6.2 . In asynchronous mode, the caller has to wait until the acknowledgement returns, which may take some time. Since the caller sends asynchronous messages in turn, the delay could accumulate to a long time. In synchronous mode, every thread is responsible for one target and they are waiting concurrently. As long as the thread pooling is used to eliminate the thread initialization time, the synchronous design is faster than the asynchronous design.

Second, the synchronous design is straightforward and easier to implement. The most convenient way for application developers is to implement the multicasting in a single method. In the asynchronous design, the responses will be returned in the callback function, so the multicasting cannot be completed in a single method. And the developers have to implement a request/response table in the application to pass the responses between the threads. This makes the implementation more complicated. As for the synchronous design, all the implementation can be put in a single method. The threads can be provided by the HAVi system. All the application developers have to do is invoke the method.

3.6.4 A New API: msgSendMultipleRequest()

Since many software elements need to send messages to multiple targets, the design should be implemented in a single method of the SoftwareElement class. The function prototype is similar to msgSendRequestSync(), making it easier to use.

Example 3-6: The function prototype of the msgSendMultipleRequest() API

37 public class SoftwareElement {

public void msgSendMultipleRequest(

SEID[] destSeid,

OperationCode[] opCode, int timeout,

HaviByteArrayOutputStream[] bufferIn, HaviByteArrayInputStream[] bufferOut, StatusHolder[] returnCode);

}

38

Chapter 4

Implementation

4.1 Environment

4.1.1 HAVi stack

We implemented a HAVi stack. For platform independence, an abstraction layer was implemented to deal with Linux specific libraries and Java Native Interface [17]. The abstraction layer was written in C and Java. The rest, the system components and the applications, were all written in Java. Thus, our HAVi stack could be easily ported to other platform with a new abstraction layer. Since all system components were written in Java, this HAVi stack is aimed at FAV devices because IAV have no Java runtime environment.

Figure 4-1: Our HAVi stack

CMM 1394

HAVi Java API

Application Application

Messaging System

DCM Event

Manager Registry

DCM Manager

Libraw1394 Linux Kernel Abstraction Layer

Implemented in Java

Implemented

in C

39

We implemented all system components except Stream Manager and Resource Manager. The following is the description of our HAVi stack. The abstraction Layer is platform dependent, providing IEEE 1394 service for MS and CMM, using Libraw1394 [18], a Linux C library, to access IEEE 1394 bus. Our CMM, MS, Registry, and Event Manager were mostly implemented as the HAVi specification defines. As for DCM Manager, only the DCM installation service was implemented.

We implemented a DCM for the DV and implemented an application to control the DV.

4.1.2 Demonstration

Two personal computers and a DV (Digital Video Camcorder) were used to build a

Two personal computers and a DV (Digital Video Camcorder) were used to build a

相關文件