DOCTORAL DEFENSE
Apr 9, 1999 1:00PM at 2-216 CST
ARCHITECTURE OF FINE-GRAINED DATA FLOW NETWORK PROGRAMMING ENVIRONMENTS
by
Yuh-Jye Chang
ABSTRACT OF DISSERTATION
This thesis focuses on the architecture of a data flow visual programming environment. An experimental system NeatTools was built to test and verify key concepts. Although the system and architecture considerations are general, software support for assisting interfaces is the main motivation for and test bed for our ideas.
The programming environment had to be implemented base on a programming language before the computer science experiments could proceed. In this thesis, I discuss the pros and cons between C/C++ and Java. Later on I discuss and compare NeatTools with Object Management Environments, Modeling Language, and some commercial visual programming tools.
ARCHITECTURE OF FINE-GRAINED DATA FLOW NETWORK PROGRAMMING ENVIRONMENTS
By
YUH-JYE CHANG
DISSERTATION
Submitted in partial fulfillment of the requirements for the degree of Doctor of Philosophy in Computer Information Science in the Graduate School of Syracuse University
Apr 1999
Approved _________________________
Professor Geoffrey Fox
Date _____________________________
Copyright 1999 Yuh-Jye Chang
All rights Reserved
CONTENTS
1. Introduction
*2. Detailed Design Considerations of NeatTools
*2.1 Project Requirements *
2.2 C++ vs. Java *
2.2.1 Java Background *
2.2.2 Benchmark *
2.2.3 Benchmark Analysis *
2.2.4 Conclusion *
2.3 How to get JAVA without using Java *
2.3.1 C++ Features that Java avoids *
2.3.1.1 Multiple Inheritance
*2.3.1.2 Operator Overloading
*2.3.1.3 Pointer
*2.3.1.4 Memory Allocation
*2.3.1.5 Fragile Super Class
*2.3.2 Some Features that Java Adds *
2.3.2.1 Build-in Thread and Synchronization Primitives
*2.3.2.2 Build-in Array
*2.3.2.3 Cross platform and Windowing API
*2.3.3 Conclusion *
2.4 NeatTools Architecture Analysis *
2.4.1 Module Abstraction *
2.4.1.1 Data
*2.4.1.2 Actions
*2.4.1.3 Connections
*2.4.2 Implementation Concept *
2.4.3 Benchmark Information on NeatTools *
2.4.4 Benchmark Analysis of NeatTools *
2.4.5 Conclusion *
2.5 NeatTools and Object Management Environments *
2.5.1 CORBA *
2.5.1.1 Object Definition
*2.5.1.2 CORBA Provided Object Interoperability
*2.5.1.3 OMA Provided Application-Level Integration
*2.5.2 COM/DCOM *
2.5.2.1 Objects and Interfaces
*2.5.2.2 Interface Description Language (IDL)
*2.5.2.3 Service Control Manager (SCM)
*2.5.3 A Comparison of CORBA and COM/DCOM *
2.5.4 Benchmark Information on CORBA *
2.5.5 Benchmark Analysis of CORBA *
2.5.6 Conclusion *
2.6 NeatTools and Modeling Languages *
2.6.1 Colored Petri-Net *
2.6.1.1 Introduction
*2.6.1.2 Why use CP-nets?
*2.6.1.3 Analysis of CP-nets
*2.6.1.4 Design/CPN
*2.6.1.5 Conclusion
*2.6.2 UML (Unified Modeling Language) *
2.6.2.1 Introduction
*2.6.2.2 UML definition
*2.6.2.3 Development Project Artifacts
*2.6.2.4 Programming Languages
*2.6.2.5 Conclusion
*2.7 NeatTools and Other Visual Programming Tools *
2.7.1 Microsoft Visual Series on C++, Basic, J++ *
2.7.1.1 Introduction
*2.7.1.2 Conclusion
*2.7.2 JavaBeans *
2.7.2.1 Introduction
*2.7.2.2 Linkage Model in JavaBeans
*2.7.2.3 Conclusion
*2.7.3 LabView *
2.7.3.1 Introduction
*2.7.3.2 Data flow Programming
*2.7.3.3 Graphical Compiler
*2.7.3.4 Multithreading
*2.7.3.5 Conclusion
*2.7.4 AVS/Express *
2.7.4.1 Introduction
*2.7.4.2 The Graphics Display Kit
*2.7.4.3 The Data Visualization Kit
*2.7.4.4 The Database Kit
*2.7.4.5 Visual Programming
*2.7.4.6 Conclusion
*3. Summary of NeatTools Implementation
*3.1 Design Goal of NeatTools *
3.1.1 Simple *
3.1.1.1 Simple Usage for the Data-flow Network Designer
*3.1.1.2 Simple Programming Paradigm for the Module Designer
*3.1.2 Object-oriented *
3.1.3 Network-ready *
3.1.4 Robust *
3.1.5 Secure *
3.1.6 Architecture Neutral *
3.1.7 Portable *
3.1.8 High-performance *
3.1.9 Multi-threaded *
3.1.10 Dynamic *
3.2 NeatTools' Visual Programming Features *
3.2.1 Introduction *
3.2.2 Multi-Thread Features *
3.2.3 Keyboard and Mouse Event Simulator/Filter *
3.2.4 Networking and TCP/IP *
3.2.5 Container Nest Structure (Complex Module) *
3.2.6 Transfer Focus among Text Fields *
3.2.7 Polymorph Data Type *
3.2.8 Multimedia Features *
3.2.9 Multimedia Database *
3.2.10 External Module and Dynamic Link Library *
3.2.11 State Machine *
3.3 NeatTools Architecture *
3.3.1 Three-Layer Architecture *
3.3.2 Package Structure *
3.3.3 OS and C++ Runtime Package *
3.3.4 Java-like API package *
4. Experiments
*4.1 Case Descriptions *
4.1.1 Eyal Sherman *
4.1.2 Brooke Kendrick *
4.2 Approach *
4.3 Limitations of Early Software *
4.4 Ideas and improvements *
4.4.1 The mouse driver prototype *
4.4.2 Next Generation Neat Software - NeatTools *
4.5 Key Issues in Building a Better Neat Software *
4.5.1 Java Like Cross Platform API in C++ *
4.5.2 Module Coordinate System *
4.5.3 Module Event Broadcast Model *
4.5.4 Increase Usability *
4.5.5 Identify the Modules *
4.5.6 Dynamic Ports *
4.5.7 Display Feedback Mechanism *
4.5.8 Cyclic Data Flow Network Issues *
4.5.9 Thread and Concurrent Related Issues *
4.6 Experiments *
4.7 Results *
4.8 Conclusion *
5. Conclusion
*5.1 Future Development *
5.1.1 Aggregate Data Type *
5.1.2 Connection Visibility *
5.1.3 Undo/Redo Features *
5.1.4 Network Support Modules *
5.1.5 Authentication Modules *
5.1.6 Visualization Modules *
5.1.7 Artistic Modules *
5.2 Conclusion *
6. Appendix
*6.1 NeatTools Reference Manual *
6.1.1 NeatTools Module Specification: *
6.1.2 NeatTools Class Hierarchy: *
6.1.2.1 LANG package
*6.1.2.2 UTIL package
*6.1.2.3 IO package
*6.1.2.4 NET package
*6.1.2.5 AWT package
*6.1.3 NeatTools application package *
6.1.3.1 NEAT package
*6.1.3.2 Modules package
*6.1.3.3 External modules package
*6.1.3.4 DESKTOP package
*6.2 Module Programming Introduction *
6.2.1 Module Event Broadcast Model *
6.2.2 Basic Methods in Module *
6.2.3 Information Methods in Module *
6.2.3.1 JString inputTag(int n), JString outputTag(int n)
*6.2.3.2 int inputType(int n), int outputType(int n)
*6.2.3.3 boolean inputAllowed(int n), boolean outputAllowed(int n)
*6.2.3.4 int inputFace(int n), int outputFace(int n)
*6.2.3.5 JFRect inputArea(int n), JFRect outputArea(int n)
*6.2.3.6 JFPoint inputPoint(int n, class JLinkObj& link), JFPoint outputPoint(int n, class JLinkObj& link)
*6.2.4 Display Related Methods in Module *
6.2.5 Module persistency related Methods *
6.2.6 Property Related Methods in Module *
6.2.7 About Polymorph Data Type *
6.2.8 Use Thread in Module *
6.2.9 Concurrency Issues when Design Module *
7. Bibliography
*
FIGURES
Figure 1: Top-level packages *
Figure 2: OS and C++ runtime layer *
Figure 3: Java like cross-platform API layer *
Figure 4: Exceptions class diagram in Java like API layer *
Figure 5: NeatTools application layer *
Figure 5: Property in NEAT package *
Figure 7: NEAT package (continue) *
TABLES
Table 1: Summary of layers *
Table 2: Classes of LANG package *
Table 3: Classes of UTIL package *
Table 4: Classes of IO package *
Table 5: Classes of NET package *
Table 6: Classes of AWT package *
Table 7: Classes of NEAT package *
A workable human computer interface can be a vital asset for disabled users who may require computers for tasks such as data collection, gesture recognition, control of external devices, virtual world control, remote collaboration, and perceptual modulation. To accomplish this, a highly configurable, easy to use, extensible, and functional system is needed. In this thesis, the architecture of a fine-grained data flow visual-programming environment is proposed for experimentation and providing solutions to this challenge.
A lot of efforts have been emphasized on using visual programming to assist the traditional texture programming. For example, the commercial visual series products like Visual C++, J++, and Visual Basic from Microsoft Corp. [8] Others focus on creating the textural programming counterpart of visual programming such as G programming language that was used to develop the LabView [23] -- a graphical programming development environment. Or, focus on data visualization by using data-flow networks like AVS/Express [22]. The visual programming architecture in NeatTools is different from all that above. It is not intended to be a visual environment that helps generating the desired application template with textural code. It does not follow the traditional textural programming paradigm such as imitating the for-loop, while-loop, process structure, function call, etc. Nor does it emphasize on a particular field of application and try to integrate the coarse-grained modules through data-flow networks. NeatTools architecture is built strictly upon the module abstraction which has its roots in the formal Input / Output automation model of Lynch and Tuttle [1]. Module is the basic building block, which the modular communication is carried out through the logical connections. Base on NeatTools architecture, a generic fine-grained visual-programming environment is implemented from a pure visual programming and high performance perspective. All modules are logically equal and they all derive from the same module base class.
In the next section, I describe the detailed design considerations of NeatTools including project requirements, platform and language issues, architecture analysis, object management environments, modeling languages, and other visual programming tools. I discuss C++ vs. Java, C++ features that Java avoids, and features that Java adds. Also, I analyze NeatTools architecture and implementation concept through the module abstraction.
In section 3, I summarize the design goal of NeatTools to be simple, object-oriented, network-ready, robust, secure, architecture neutral, portable, high-performance, multi-threaded, and dynamic. I also introduce visual programming features such as multi-thread, keyboard/mouse event simulator/filter, networking, container nest structure, polymorph data type, multimedia, database, external module and state machine. Then, I discuss the NeatTools architecture including the three-layer architecture, package structure, OS and C++ runtime package, and Java-like API package, etc.
In section 4, the experiments were carried out through Eyal Sherman and Brooke Kendrick's case. I have described my approach to these cases, the limitations of early software, my ideas and improvements over the early software, and key issues in building better Neat software. Later, the experiments were presented and encouraging results were achieved.
NeatTools is an experimental computer science project. Before we can undertake any experiment, we need to have a tool or an environment. This makes it possible to build and test design concepts and proceed with the experiment itself. So, the first step was to implement such a visual programming tool. The question was which language to use to implement the tool so it would meet all the requirements and still have the potential for future development. C/C++ is the industry standard and Java is a rapidly developing and widely adopted new language. This section discusses the pros and cons of these two popular languages.
Java is an object-oriented programming language developed by Sun Microsystems, a company best known for its high-end UNIX workstations. Modeled after C++, the Java language was designed to be small, simple, and portable across platforms and operating systems, both at the source and at the binary level.
Platform independence is one of the significant advantages that Java has over other programming languages. At the source level, Java's primitive data types have consistent sizes across all development platforms. Java's foundation class libraries make it easy to write code that can be moved from platform to platform without the need to rewrite it to work with another platform. Java binary files are also platform-independent and can run on multiple platforms without the need to recompile the source. How does this work? Java binary files are actually in a form called bytecodes. Unlike most other programming languages, The Java development environment has two parts: a Java compiler and a Java interpreter. The Java compiler takes the Java program and instead of generating machine code from source files, it generates bytecodes. To run a Java program, a user runs a program acting as a bytecode interpreter, which in turn executes a Java program. The user can either run the interpreter by itself, or for applets, there is a bytecode interpreter built into a Java-capable or enabled browser like Netscape [29] or Microsoft Internet Explorer [8] which runs the applet.
The disadvantage of using bytecodes is in execution speed. Because system-specific programs run directly on the hardware for which they are compiled, they run significantly faster than Java bytecodes which must be processed by the Java interpreter. In order to increase the performance of Java bytes, some companies are dedicated to accelerating the bytecode. JIT [25] (just in time) interpreter is one of the technologies that is widely adapted into commercial Web browsers. JIT interpreter usually loads the Java bytecodes, preprocesses it into system-specific code in a dynamical fashion, and then executes the system-specific code directly.
I wrote a simple Java benchmark program:
import java.applet.Applet;
public class benchmark extends Applet {
public int dummy(int i) { return i;}
public void start() {
int i, x;
x = 0;
System.out.println("Benchmark Start");
long b1 = System.currentTimeMillis(), b2;
for (i=0; i<100000000; i++) {
}
b2 = System.currentTimeMillis();
System.out.println("Empty For Loop "+(b2-b1));
b1 = b2;
for (i=0; i<100000000; i++) {
x += i;
}
b2 = System.currentTimeMillis();
System.out.println("For Loop with + and assign "+(b2-b1));
b1 = b2;
for (i=0; i<100000000; i++) {
x *= i;
}
b2 = System.currentTimeMillis();
System.out.println("For Loop with * and assign "+(b2-b1));
b1 = b2;
for (i=0; i<100000000; i++) {
x += dummy(i);
}
b2 = System.currentTimeMillis();
System.out.println("For Loop with function call and assign "+(b2-b1));
}
}
Translating the Java program into C++ would be:
#include <stdio.h>
#include <sys/timeb.h>
long currentTimeMillis() {
struct _timeb tstruct;
_ftime(&tstruct);
return tstruct.time*1000+tstruct.millitm;
}
class benchmark {
public:
int dummy(int i) { return i;}
void start() {
int i, x;
x = 0;
printf("Benchmark Start\n");
long b1 = currentTimeMillis(), b2;
for (i=0; i<100000000; i++) {
}
b2 = currentTimeMillis();
printf("Empty For Loop %d\n", b2-b1);
b1 = b2 = currentTimeMillis();
for (i=0; i<100000000; i++) {
x += i;
}
b2 = currentTimeMillis();
printf("For Loop with + and assign %d\n", b2-b1);
b1 = b2 = currentTimeMillis();
for (i=0; i<100000000; i++) {
x *= i;
}
b2 = currentTimeMillis();
printf("For Loop with * and assign %d\n", b2-b1);
b1 = b2 = currentTimeMillis();
for (i=0; i<100000000; i++) {
x += dummy(i);
}
b2 = currentTimeMillis();
printf("For Loop with function call and assign %d\n",
b2-b1);
}
};
void main() {
benchmark bm;
bm.start();
}
The test data are below (Test on PC with AMD-233MHZ CPU[30], Win95) :
Units (ms) |
for loop 108 |
x += i |
x *= i |
x += dummy(i) |
C++, VC++ |
0 * |
880 |
1810 |
880 |
JDK 1.1 [25] |
29000 |
50420 |
51300 |
110730 |
JDK 1.0 |
43830 |
82340 |
83210 |
170210 |
IE 4.0 |
42400 |
63330 |
63820 |
175550 |
IE 4.0 (JIT) |
0* |
930 |
2250 |
9010 |
Netscape 4.05 |
940 |
1810 |
2690 |
8510 |
* Optimized and eliminated by compiler or interpreter.
Units (us/loop) |
for loop |
x += i |
x *= i |
x += dummy(i) |
C++, VC++ |
0 * |
0.0088 |
0.0181 |
0.0088 |
JDK 1.1 |
0.29 |
0.5042 |
0.513 |
1.1073 |
JDK 1.0 |
0.4383 |
0.8234 |
0.8321 |
1.7021 |
IE 4.0 |
0.424 |
0.6333 |
0.6382 |
1.7555 |
IE 4.0 (JIT) |
0* |
0.0093 |
0.0225 |
0.0901 |
Netscape 4.05 |
0.0094 |
0.0181 |
0.0269 |
0.0851 |
* Optimized and eliminated by compiler or interpreter.
Based on the nature of object oriented programming, a function call is a must. Class interfaces are actually functions associated with objects. From the table above we learn that the most advanced commercial browser's Java interpreter is about 10 times slower than compiled C++ code when a function call is involved in the loop. And when the Java interpreter is not equipped with JIT, the ratio becomes unacceptably slower (around 200 times slower). And this is only a very simple benchmark program. When it comes to large applications, the JIT tends toward worse performance because it has a limited buffer for compiled code. If the whole application is too large to be compiled before it executes, the JIT interpreter will have to compile section by section on the fly and will thus slow down the execution speed. I estimate the JIT would run about 20 times slower than compiled C++ code when an application is relatively large. But, Java is changing and evolving rapidly compared to C/C++; JDK itself has gone through several changes and enhancements including JDK 1.1, JDK 1.2 [25], JDBC [25], Java 3D [25], JIT; and the long awaited Java compiler, HotSpot [25], will be available in 1999. A lot of experts in this field believe that it is possible that Java could increase in speed, performance, and eventually exceed what C/C++ does in the future. This means that the benchmark result today may not be similar in the future, but it could state the facts from the past until now.
When I started to design the next generation of the NeatTools project, I insisted on using C++ instead of Java. The main reason for that is speed and Java's uncertainty in the future. At that time, the JIT technology was not mature. Even today the JIT Java interpreter is still not fast enough for mission critical and calculation intensive real-time tasks like compress/decompress of voice and video data. However, like I discussed before, the benchmark result could only reflect Java as an immature technology at this time. It might not always be true in the future. Today, there are some other solutions such as linking native code into Java programs or using tools to convert Java bytecode into native code. But these alternatives are still not very efficient or stable. With the urgent need to help disabled people and solve other problems in the NeatTools project, my choice to use C++ was appropriate, because, I could then focus on the unique features and strategies for specific applications and experiments in NeatTools, not the language issue itself. In the future, when Java does become superior to C/C++, we may choose to rewrite NeatTools and adapt all the experience we have acquired towards implementing them in Java.
Here I would like to discuss some architecture issues. First, I will list the C++ features that Java avoids and investigate why they might be favorable. Actually, a programmer could benefit from those features when those features are well understood and used properly. Secondly, some features that Java adds could be useful. I will discuss how to implement the C++ counterpart and provide the desired functionality to the programmer.
Java provides single inheritance that is a subset of C++ multiple inheritance. Java class could "extends" from one super class and could "implements" multiple interfaces (abstract classes without any implementation and non-static data members). In C++, a class could inheritance from a set of classes including abstract classes (classes with one or more functions declared as abstract) or regular classes.
In theory, Java could provide the functionality of multiple inheritance by implements multiple interface classes. But in practice it could lead to code reusability problems. Lets look at the following example.
In Java 1.1, the new API provides a new mechanism that could make event broadcasting and listening more natural. This is a good use of object oriented programming. In the AWT class, the user usually extends from Canvas or Panel class to provide a windowing class with specialized functionality. In order to provide the basic action event broadcasting, the user usually has to implement the following methods like addActionListener(), removeActionListener(), and broadcastEvent(). The code is as follows:
ActionListener actionListener = null;
public void addActionListener(ActionListener l) {
actionListener = AWTEventMulticaster.add(actionListener, l);
}
public void removeActionListener(ActionListener l) {
actionListener = AWTEventMulticaster.remove(actionListener, l);
}
public void broadcastEvent(ActionEvent e) {
if (actionListener != null)
actionListener.actionPerformed(e);
}
The code above is simple and straightforward. Because not every windowing class needs to dispatch action events, these methods are not provided with the Component class (the super class for every windowing class). Now, there is no way for the Java programmer to reuse the code in different classes with different inheritance paths because of Java's single inheritance. For example, class A and class B are derived from Component and both classes need to broadcast action events. Then, both classes need to implement the exact same code segment and could not just inherit it from a separate interface (the interface does not carry implementation, only method declarations).
By using a different design pattern, one may design a separate class ActionBroadcast that can handle the event broadcast and include the class as a data member. That way we can wrap the methods, and the member class variable provides the actual implementation.
ActionBroadcast broadcast = new ActionBroadcast();
public void addActionListener(ActionListener l) {
broadcast.addActionListener(l);
}
public void removeActionListener(ActionListener l) {
broadcast.remvoeActionListener(l);
}
public void broadcastEvent(ActionEvent e) {
broadcast.broadcastEvent(e);
}
This solution solves the code reusable problem and, in the mean time, leads to an inefficient coding style. Because the method wrapper is always slower than a direct function call. When the underlying implementation is complex, the wrapper pattern will not account for too much performance loss. For the example above, this approach does not provide any benefit in terms of code reusability or efficiency.
In Java, operator overloading is eliminated completely. Though, the effects of operator overloading can be achieved by declaring a class, appropriate instance variables, and appropriate methods to manipulate those variables. I believe good use of operator overloading could actually increase the readability and sometimes the efficiency of the program.
For example, in some arithmetic classes like Vector, Matrix, and Coordinate, the use of operator overloading like addition, subtraction, multiplication, and division are very natural and could lead to a much more concise syntax. Without operator overloading, some complex calculations could become ugly and almost unreadable. Sometimes, one line of program expressed by variables and operators has to rewrite into several lines when expressed by variables and methods. This situation could cause the compiler to generate less optimized code and decrease efficiency.
Let's say we have Point3D as a 3D coordinate, and we want to calculate the point rotation with angle alpha. In Java, we will need to define the mul(double) add(Point3D) methods by the following statement:
Point3D p1 = new Point3D(1, 0, 0);
Point3D p2 = new Point3D(0, 1, 0);
p1.mul(Math.sin(alpha)).add(p2.mul(Math.cos(alpha)));
In C++, we define the operator*(double) and operator+(Point3D) methods and the statement becomes:
Point3D p1(1, 0, 0);
Point3D p2(0, 1, 0);
p1*sin(alpha)+p2*cos(alpha);
As you may notice, in C++, the statement is much shorter and easier to understand. Also, the notion of operators is natural to how we express mathematic equations. For complex Matrix or Vector operations, the statement in Java could become very long and unreadable. I believe, for a project group or for someone else to read the code, readability is a very important factor. In C++, we have the flexibility accomplish this by operator overloading or just by methods.
The main reason Java eliminates pointer is that pointers are one of the primary features that enable programmers to inject bugs into their code. But, to my understanding, object oriented design could avoid class user's pointer usage completely. Also, good use of pointer could increase the efficiency and simplify the code generation.
In Java, the only way that a method could return primitive data type values in its parameters is through the use of Array.
int arg[] = new int[1];
foo(arg);
In C++, the same code could be expressed as:
int arg;
foo(&arg);
Obviously, the implementation in C++ is much better then Java. In Java, it requires more memory space (both reference for arg reference and int[] array class instance) and also it needs to implicitly initialize the internal Array class instance (which has unavoidable error checking code in Java API) for "arg" as an array of integers.
For those situations that pointer exposure is unwanted, we could use object class to hide the pointer and, in the mean time, provide the functionality needed. For example, to avoid user handle pointer directly, we could design IntArray class:
class IntArray {
int *data;
public:
IntArray(int size);
~IntArray();
int operator[](int index);
};
The IntArray class could encapsulate the integer pointer inside the class. It keeps the class user from using the pointer directly. In the mean time, it could allocate and de-allocate the memory properly in constructor and destructor. So, we don't have to worry about the pointer usage and memory allocation all together.
In the "Java white paper" [25], it states:
"Java completely removes the memory management load from the programmer. C-style pointers, pointer arithmetic, malloc, and free do not exist. Automatic garbage collection is an integral part of Java and its run-time system. While Java has a new operator to allocate memory for objects, there is no explicit free function. Once you have allocated an object, the run-time system keeps track of the object's status and automatically reclaims memory when objects are no longer in use, freeing memory for future use."
In theory, humans can always design better algorithms in compiler or interpreter to optimize the performance of high-level language. But Assembly language is still there to maximize the performance for some mission critical tasks in specialized computer industry. The most advanced-programming wizard could create quite a functional skeleton source code, but when it comes to specific requirements, an experienced programmer could still do a better job. Here, I try to express the idea that the human factor could never be eliminated in the decision making process. In the memory management case, according the different requirements and constraints, we can decide to implement a garbage collector in C++ or use object oriented programming to hide the memory management inside an object and distribute the memory management efforts into the object hierarchy. The programmer could use the class and never have to deal with the memory management problems directly. We can also decide to implement a reference count to eliminate unnecessary memory duplications, speed up the performance, and minimize the space requirement. In short, with proper set of classes support, C++ could become robust and free of memory leak problems. There are a great deal of different algorithms and implementation that we can choose in C++ or other object oriented programming languages that provide memory management primitives, but in Java we lose the right to choose. In C language's design spirits, it trusts programmers that they could do their job right. In Java, it only trusts the system designer. When a system designer fails to make good implementations, Java programmers have no choice but to use it anyway. For example, in Java3D, the API itself is still in beta test and has several bugs. One of the most complained about issues is that it consumes too much memory even when users only construct a fairly simply scene. I believe part of the reason lies in Java's memory management implementation. The garbage collector could automatically reclaim the unused memory. But the programmer's convenience comes with a price - we could nullify a class reference right away, but we don't know when it will be collected and reclaimed. For those big chunks of memory blocks (texture map, image buffer, or z buffer), they need to be claimed and reclaimed in the right order and in the right time. In this case, the garbage collector can not perform well and usually will cause memory usage problems.
Fragile super class problems happen when a programmer modifies the header file of a base class; all the derived class which use this header file will need to be recompiled, or the executable linkage problem could occur. I believe this problem is inherited in the implementation of the current C/C++ compiler, not in the language itself. C/C++ compiler tends to parse the method name and data member identifier into an internal address. When the structure or the order of the identifier changes in the header file, the internal address and the lookup table change also. Without re-compiling the derived class's source code, the internal address of the methods or data members will not match. Thus, the fragile super class problem occurs - the programmer has to re-compile the super class before it could be linked to the derived classes. With a proper naming lookup protocol, this problem could be solved. On the other hand, a better way to solve the problem is to avoid changing the super class's interface. I believe, before the coding draft, a detailed class design and analysis will ensure a proper interface of a super class. Thus, we could avoid modifying the base class header file and prevent the fragile super class problem from happening.
By using object oriented programming, multithreading capabilities could be added in C++ easily. The usage could be similar to what Java has. In the Thread class, we can have a class that uses Thread primitive (In Microsoft Windows, the Win32 API already has build-in thread support. In most UNIX systems, the pThread [31] is an add-on package to support the POSIX [31] thread) to create a new Thread and store its handle in the data member of Thread class. Later on when the thread is ready to start, we can redirect the thread process flow to the run() method. Thus, when user derives class from Thread class or Runnable class, it could then override the run() method and implement its thread loop. In "Java Like Cross Platform API," I follow the Java API standard and implement the JThread class in C++. It was proved that the API could hide the platform dependent implementation nicely, and programmers could have multithreading capabilities without noticing its underlying implementation differences.
To deal with concurrent problems, Java defines a new method modifier to integrate the thread synchronization into language. I believe this is a good move. But in C++, we can implement the mutex and critical section control into utility classes to help us handle the thread synchronization problems. In this case, we could have flexibility with how we want our process flow to be guarded by those synchronization primitives and make suitable adjustments to adapt to the needs of different situations.
In Java, the Array class was created implicitly. The "new Data_Type[size]" syntax allows the user to create Array of any data type. It is very convenient, as there is no need to make type casting for the returned index elements. In the old-fashioned C++ design, the only way to make an Array is using the pointer of object pointer to implement the object array:
class ObjectArray {
int length;
Object **data;
public:
ObjectArray(int size);
~ObjectArray();
Object* operator[](int index);
};
Here, the polymorphism in C++ allows us to use ObjectArray to store any class that is derived from Object class. But the element returned by operator[] could only return a pointer of Object which needs to be cast before it could be used properly. Also, it could not store primitive data type and other classes which do not derive from Object class. So, in practice, it is not a good solution for a universal array.
Fortunately, C++ now includes a "template" which could embed the data type as a parameter into the class declaration and solve the problem for an easy usage universal array. The template could be declared as follows:
template <class DataType>
class ObjectArray {
int length;
DataType *data;
public:
ObjectArray(int size);
~ObjectArray();
DataType& operator[](int index);
};
Now, the same template could apply to any class and primitive data type as well. Also the elements return by operator[] will return the desired data type. So, the use of template could provide exactly the same functionality that Java's build-in array provides.
In C++, the windowing related task is never easy. I remember, in the early books that discussion of how to open a simple window and display "Hello World!" in any Window system was fairly complex. The program was usually more than 100 lines. Since C++/C was designed for environments with only text input/output, windowing was not natural and straightforward.
Later on, as the windowing environments became more and more popular, most of the C/C++ compilers began to provide the IDE (Integrated Development Environment) with graphical user interfaces running on a windowing environment. Also, compilers came with API (like OWL [32], MFC [8], and MOTIF [33]) that assist the programmer in creating their own graphical user interfaces. The problem is APIs are very different from one another. Also, for different windowing systems, there are all sorts of different APIs.
As the Internet becomes more prevalent, the interaction between different hosts running different operating systems and locations becomes more and more common and hence more and more important. Java was the first language, which supported cross platform development on the language level. Java's virtual machine and byte code system is the natural result of the language specification to be a cross platform language. I have discussed the cons and pros of this design. My solution to cross platform for C++ is an API that provides uniform support to basic utility and abstract windowing operations. It could encapsulate the underlying implementation, which is based on the windowing environment. So application dependency to windowing environment is eliminated, and the goal of cross platform in C++ could be easily achieved.
In this section, I examined the C++ features that Java avoids and discuss how to avoid the potential pitfalls. Instead of eliminating them, we could try to understand them, making good use of them and benefiting from those features. Overall, C++ is more flexible than Java. It provides multiple inheritance, operator overloading, and pointer. It could add code reusability, readability, and efficiency. For those userful features that Java adds, I propose a practical way to provide them in the C++. So C++ programmers could get Java's features without using Java. My conclusion is: object oriented programming in C++ is so powerful that it could be used to avoid commonly seen problems and also it could be extended with new kinds of functionality as well.
In NeatTools, the most important concept is module abstraction. The module abstraction simplifies and models all the interactions between modules into active actions and reactive actions. Later in this section, I will discuss the implementation concept and present some benchmark information and analyze it.
In NeatTools, module abstraction is offered as a set of class methods that provide inter-module communication functionality. Functional components (implemented as class objects) of a concurrent system are written as encapsulated modules that act upon local data structures, or objects inside object classes, some of which may be broadcast for external use. Relationships among modules are specified by logical connections among their broadcast data structures. Whenever a module updates data and wishes to broadcast the change, and make it visible to other connected modules, it should implicitly call an output service function which will broadcast the target data structure according to the configuration of logical connections. Upon receiving the message event, the connected modules execute an action engine according to the remote data structure. Thus, output is essentially a byproduct of computation, and input is handled passively, treated as an instigator of computation.
This approach simplifies module programming by cleanly separating computation from communication. Software modules written using module abstraction do not establish or effect communication, but instead are concerned only with the details of local computation. Communication is declared separately as logical relationships among the state components of different modules.
This programming model has its roots in the formal Input / Output automaton model of Lynch and Tuttle [1]. An I/O automaton is a state machine with a signature consisting of a set of input actions and a set of locally controlled actions. The locally controlled actions could divide into output actions and internal actions. Locally controlled actions are under the control of the automaton, while input actions may occur at any time. Automata may be composed such that when an output action of one automaton occurs, all automata having a same-named action as an input action may make a state transition simultaneously. A behavior of an I/O automaton is a sequence of input and output actions that may occur in an execution of that automaton. The module abstraction programming model is designed to benefit from the useful characteristics of the I/O automaton model that are helpful in reasoning formally about distributed systems.
Module abstraction is based on three fundamental concepts: data, actions, and connections. It is difficult to discuss these concepts in detail without reference to particular mechanisms for supporting them. Therefore, we present them in the context of NeatTools, a software run-time system and visual programming environment designed to support the development of data flow network applications using module abstraction.
Data (the components of a module's state, could be data structures or objects) may be kept private, or they may be broadcast when needed so that other modules may access the data. NeatTools provides a base abstract class object that declares the basic data structure and service procedures (or methods). Every module object should inherit from this class object and override some predefined procedures to serve the different computational and presentational needs of each module. NeatTools provides a library of data types for declaring data that may be broadcast. These include but are not limited to integer, real, string, block, byte array, midi event, voice stream, and video stream. The module programmer may define others.
Each NeatTools module has a presentation that presents itself to the user as visual feedback. It could be graphical images, shapes, or text. The presentation may change dynamically according to the current state. Associated with each data item are a public name, property, access privileges, and data type. This information helps a user understand its presentation. The data type and access privileges also permit type check and privilege match of logical connections.
Access privileges include input, output, insert, and connect. Output access allows connected modules to observe the value of the data when broadcast as an event message. Input access allows a module to change the state of the target module. Insert access allows a new connection to be inserted into an aggregate item of a module. Connect access allows a module to relate the data item to a data item of some other module. All those access privileges are controlled by a set of class methods (already defined in the base abstract class). By overriding those methods, modules could change the behavior or dynamically control access behavior according to the current state of the module.
A NeatTools module interacts with a desktop and a collection of other modules that may be unknown to this module but that read and modify the data item in its presentation when permitted by the access privileges. The desktop also works as a graphical user interface front end that provides a user with a set of layout service functions, including move, resize, copy, delete, group, ungroup, connection management, object persistency management and file input/output.
The action portion of a module defines how its state changes over time or responds to an environment. Insulated from the structure of its environment, a NeatTools module interacts entirely through the broadcast service and reactive execution engine procedures. A module may autonomously modify its local state, and also it may react to the incoming events and then change its local state. This suggests a natural division of the actions into two parts: active action and reactive action.
The active action carries out the ongoing computation of the module. For example, in a discrete event simulation, the active action would be iterative computation that simulates each event. External updates of simulation parameters could affect the course of future iterations, but would not require any special activity at the time of each change. Modules with only active action can be quite elegant since input simply steers the active computation without requiring a direct response. Active action is analogous to the locally controlled actions of an I/O automaton.
The reactive action carries out activities in response to input from the environment. A module with primarily reactive action simply reacts to input from the environment, updating its local state and presentation as dictated by the input change. For example, a data visualization module could be constructed so that each time some data element changes, the visualization is updated to reflect the change. In the above discrete event simulation, one might add reactive action to check the consistency of simulation parameters that are modified by the environment. Reactive action is analogous to the input actions of an I/O automaton.
Relationships between data items of different modules are declared with logical connections between those data items. These connections define the communication pattern of the system. Connections are established by a special NeatTools module, called a desktop, that enforces type compatibility across connections and guards against access protection violations by establishing only authorized connections.
Connections are declared separately from modules so that one can design each module with a local orientation and later connect them together in various ways. Connections are designed to accommodate all kinds of data types varying from integer and real number to audio and video. The run-time system could handle the communication requirements automatically according to the module abstraction of the module.
If we liken the data items of a NeatTools module to the actions in the signature of an I/O automaton, then just as like-named actions in automaton signatures define the sharing of actions, connections define the sharing of state change information. Currently, a simple synchronous data transmission algorithm is used by the broadcast service procedure. The reactive action engine of a connected module will be invoked and executed automatically. However, if asynchronous data transmission is needed, a user could construct a data queue inside the module and react to the data queue later. This way, we can keep the general communication structure simple and efficient.
The NeatTools' design concept is based on a special aspect of application design, which provides visual programming capacity. In traditional textual programming design, lines of code are the rough measure of program size. In a visual programming design like NeatTools' data flow networks, the number of connections and modules become the major indicators. Thus, how NeatTools provides efficient way to broadcast and process message events between modules becomes the most important issue. In NeatTools, all modules are already class objects. The only object interoperability is simplified into active and reactive actions, or connections. In order to increase the application throughput, the connections in NeatTools are just logical references. The message broadcasts are actually performed by direct function calls to ensure the performance of NeatTools' data flow network. Remote messages passing between NeatTools and a remote computer running NeatTools are performed by Socket and ServerSocket modules (just like regular modules, but they perform different tasks) to handle all the remote data communication tasks (usually on top of TCP/IP which is slow compared to a direct function call). Connections between Socket and other modules still use the same efficient function call. So, instead of a network connection oriented design (like CORBA [20]), NeatTools uses a direct function call oriented design that eliminates all possible overhead and layers.
I wrote a special benchmark module in NeatTools, which has two inputs and one output. The one on top can be enabled to start the benchmark. The one on the left can receive message events from other benchmark modules but do nothing. When the module is enabled, it will start the benchmark by broadcasting events through its output port in a 106 loop and then display the time interval in the debug dialog box. The benchmark here is focused on the overhead of message broadcasting in NeatTools.
The engine method of benchmark module was implemented as follows:
void JBenchmarkObj::engine(int n, JLinkObj& link) {
if (!n) {
// link.access(JIntegerData(v[0]));
} else {
int oldv = v[1];
link.access(JIntegerData(v[1]));
v[1] = (v[1] != 0);
if ((v[1] != oldv) && !v[1]) {
int time = JSystem::currentTimeMillis();
for (int i=0; i<1000000; i++)
broadcast(0);
time = JSystem::currentTimeMillis()-time;
JComponent::debug(JInteger::toJString(time)+" ms");
}
}
}
The NeatTools benchmark data flow networks are arranged as follows:

The five test modules are instances of a benchmark module. One of them connects its enabled input from a button, and its output connects to the rest of the test modules.
The benchmark data for this particular data flow network running on my PC (AMD-233MHZ CPU, Win95) is 2200 ms, because the output has four connections and the broadcast processes are repeated for 106 times. So 2200 ms / 4 / 106 becomes 0.55 us per connection broadcast which is just 6.25 times slower compared to the direct function call with only one statement. In other words, NeatTools is capable of dispatching around 1,800,000 messages in one second. At this speed, I believe NeatTools could handle most real-time or computationally intensive tasks on the data flow network level.
The purpose of this section was to focus on how I proposed an abstract module model. Based on this model, I introduced a simplified and high performance way to implement the module connections, and how to map the connections into direct function call without much overhead or layers. This model proved successful as implemented in the NeatTools project. With the scalable design of the NeatTools project, I believe NeatTools could be widely adapted to most real world problems and provide complete and fast solutions.
Object Managements Group [20] (OMG)'s Object Management Architecture (OMA) is the multi-vendor standard for object-oriented distributed computing. This includes CORBA -- the Common Object Request Broker Architecture -- which most people associate with OMG; CORBAservices and CORBAfacilities.
Objects are discrete software components -- they contain data, and can manipulate it. Usually, they model real-world objects, although sometimes it's useful to create objects specifically for items we want to compute. Other software components send messages to objects with requests; the objects send other messages back with their responses.
In order for objects to plug and play together in a useful way, clients have to know exactly what they can expect from every object they might call upon for a service. In CORBA, the services that an object provides are expressed in a contract that serves as the interface between it and the rest of our system. The interfaces are expressed in OMG Interface Definition Language -- OMG IDL -- making them accessible to objects written in virtually any programming language, and the cross-platform communications architecture is the Common Object Request Broker Architecture -- CORBA.
Based on CORBA architecture, the OMA specifies a set of standard interfaces and functions for each component. Different vendors' implementations of the interfaces and their functionality then plug-and-play on customers' networks, allowing integration of additional functionality from purchased modules or in-house development.
The OMA is divided into two major components: lower-level CORBAservices and intermediate-level CORBAfacilities. The CORBAservices provide basic functionality that almost any object might need: object lifecycle services such as move and copy, naming and directory services, and other basics. The CORBAfacilities architecture has two major components: one, horizontal, including facilities such as compound document services which can be used by virtually every business; and the other, vertical, standardizing management of information specialized to particular industry groups.
COM [8] (Component Object Model) is a software architecture that enables a program to be built from smaller binary components. It is a binary standard for component interoperability and is independent of any programming language.
COM supports a client/server model between the user of some object's services and the implementers of that object and its services. COM's role is to establish the connection between the client and the server, which offers the desired object. Once the connection has been made, COM is out of the picture and all communication goes directly from server to client and vice versa.
In COM an object is an instance of a class which as in standard OO terminology is a set of data and related functionality. Unlike most other OO models, COM provides no direct access to object data. Instead, users have to access member functions of an associated interface.
An interface is a set of functions that can be invoked on a given object. Interfaces do not contain any implementation what so ever, but merely define the expected behavior of an object. When an object implements an interface, it provides implementations for every function in that interface, and provides pointers of those functions to COM.
COM's Interface Description Language (IDL) is based on the Open Software Foundation (OSF) [33] Distributed Computing Environment (DCE) [33] specification for describing interfaces, operations, and attributes to define remote procedure calls.
A designer can define a new custom interface by writing an interface definition file. The interface definition file uses the IDL to describe data types and member functions of an interface. The interface definition file contains the information that defines the actual contract between the client application and the server object.
The SCM is a COM component that is able to locate a given server and launch it. The SCM contains a database of class information. When a client requests the COM library to create an object, the SCM is launched, the server located and run. Here SCM provides the object interoperability.
From the communication point of view, COM is a little bit different from CORBA. In CORBA, ORB is always the gateway between its object client and server. The client and server in CORBA never communicate with each other directly. In COM, SCM locates and launches the object server for the application client, and then the client communicates directly with the server. Also COM has different mechanisms for in-process, cross-process, and remote object servers. In in-process cases, the SCM will locate and load the object server as a DLL. So in this situation, the object server will be executed in the client's process space, which is much faster than CORBA's mechanism. In cross-process cases, the SCM will locate and load the object server as an executable. The object server will be executed in a separate process space. So it is slower than the DLL but still faster then the CORBA's mechanism. Even in remote object server cases, the SCM will contact the remote SCM and later on build a remote proxy server which can forward requests directly to the remote SCM via the RPC connection. So, in general, COM/DCOM has better performance than CORBA. But CORBA has better design based on system and structured concerns. For example, a CORBA's object server will only deal with its local ORB. But a DCOM's object will have to deal with local SCM directly, or in-process client, cross-process client, and remote client through an interface wrapper.
From the application point of view, COM is lacking in application integration packages. CORBA's CORBAservices and CORBAfacilities components provide application-level integration which in turn provides lower-level and intermediate-level services for industry and business applications.
The most important advantage that Object Management Environments like CORBA, and COM/DCOM is object interoperability. Object interoperability allows objects, which plug and play together in a useful way to become possible. But the advantage comes with a price -- speed. For example, in CORBA, when an object client issues a function call to a remote server object, it has to go through the IDL Stub (created by IDL), Object Request Broker (ORB), remote ORB, IDL Skeleton, and finally reaches the remote server object implementation. And in most cases it goes through a network communication layer like TCP/IP at least twice (forward the request and send back the result message) to complete a function call.
I visited one of the ORB vendor sites which has a web page dedicated to performance information. The URL for this page is http://www.orl.co.uk/omniORB/omniORBPerformance.html. From this page, came one of the tables below:
Performance of omniORB2 on various platforms.
Platform |
Transport |
Protocol |
us/call |
LINUX Pentium Pro 200 MHz |
IP/intra-machine |
IIOP |
340 |
|
|
IP/ethernet (ISA) |
IIOP |
1000 |
|
|
IP/ATM |
IIOP |
440 |
|
|
AAL5/ATM |
IIOP |
350 |
Windows NT Pentium Pro 200 MHz |
IP/intra-machine |
IIOP |
360 |
|
|
IP/ethernet (ISA) |
IIOP |
1000 |
Digital UNIX 3.2 Alpha DEC 3000 |
IP/intra-machine |
IIOP |
750 |
|
|
IP/ethernet |
IIOP |
1050 |
Windows '95 Pentium 166 MHz |
IP/intr-machine |
IIOP |
1000 |
|
|
IP/ethernet (PCI) |
IIOP |
1250 |
SOLARIS 2.51 Ultra 1 167 MHz |
IP/intra-machine |
IIOP |
540 |
|
|
IP/ethernet |
IIOP |
710 |
From the table above, to make a function call, even if the function call is made within the machine (the IP/intr-machine), it takes about 360 us on a Pentium Pro 200MHz machine. If we compare the benchmark information with the previous section, making a direct function call in compiled C++ code only takes 0.0088 us which is about 41,000 times faster than making function calls on this particular ORB. For those calls that are made remotely by a direct network card connection (the IP/ethernet), it takes 1,000 us to make a function call. In that case it is about 114,000 times slower than the compiled C++ code.
CORBA and COM/DCOM provide object interoperability, which is a great feature for object plug and play and object reusability. In CORBA, it especially tries very hard to separate the object server from object client to ensure encapsulation and increases the object interoperability by using ORBs. But this approach comes with a big draw back -- decreased performance to a great degree. In COM/DCOM, the performance problem is less severe. Because COM/DCOM provides direct access to the object server when an object server is loaded as an in-process DLL. But, even though COM/DCOM is independent of the programming language, some of the higher level control mechanisms involve registry operations in Microsoft Windows like Win95 or NT which we try to avoid in NeatTools due to cross platform concerns.
It is possible that for the NeatTools project to be built upon CORBA architecture. A NeatTools module designer will be able to put the modules on line and allow user remote access. But, NeatTools will have to limit data flow design with modules that do not require a lot of message event passing. For those fine-grained modules such as digital logic, number, and real number operation modules, decreasing the message passing speed will greatly hurt the performance. In this case, we won't be able to build data flow networks in NeatTools complex enough to meet the requirements of a general human interface tool. Other than the performance concern, every component in CORBA or COM/DCOM needs to dedicate some efforts and space on the interfacing, because the interfacing is where the object interoperability comes from. NeatTools has simplified broadcast models, as the interface is actually fixed and derived from the JModuleObj class. That means we have to dedicate identical interfacing by using IDL for each module, which is actually a redundant layer and could be eliminated. Thus, based on the space and speed constraints, I would suggest not using CORBA or COM/DCOM to develop NeatTools.
The following material regarding Petri-Net is extracted from the web page at the URL http://www.daimi.aau.dk/PetriNets/.
Colored Petri-Net (CP-nets or CPN) is a graphical oriented language for design, specification, simulation and verification of systems. It is in particular well-suited for systems in which communication, synchronization and resource sharing are important. Typical examples of application areas are communication protocols, distributed systems, imbedded systems, automated production systems, work flow analysis and VLSI chips.
CP-nets are used for three different - but closely related - purposes. First of all, a CP-net model is a description of the modeled system, and it can be used as a specification (of a system to be built) or as a presentation (of a system to be explained to other people, or ourselves). By creating a model, we can investigate a new system before we construct it. This is an obvious advantage, in particular for systems where design errors may jeopardize security or would be very expensive to correct. Secondly, the behavior of a CPN model can be analyzed, either by means of simulation (which is equivalent to program execution and program debugging) or by means of more formal analysis methods (which are equivalent to program verification). Finally, it should be understood that the process of creating the description and performing the analysis usually gives the model designer a dramatically improved understanding of the modeled system -- and it is often the case that this is more valid than the description and the analysis results themselves.
CP-nets can be analyzed in three different ways:
The first analysis method is simulation. It is very similar to debugging and program execution. This means that we can execute a CP-net model (e.g., to get statistics about the behavior of the modeled system). It is possible to set breakpoints and to display the simulation results by means of different kinds of business graphics.
The second analysis method is occurrence graphs (also called state spaces or reachability graphs). The basic idea behind occurrence graphs is to construct a directed graph, which has a node for each reachable system state and an arc for each possible state change. Obviously, such a graph may become very large, even for small CP-nets. However, it can be constructed and analyzed totally automatically, and there exist techniques which makes it possible to work with condensed occurrence graphs without losing analytic power. These techniques build upon equivalence classes.
The third analysis method is place invariants. This method is very similar to the use of invariants in ordinary program verification. The user constructs a set of equations, which provides the satisfaction of all reachable system states. The equation is used to prove properties of the modeled system (e.g., absence of deadlock).
Design/CPN is a tool package supporting the use of CP-nets. The Design/CPN tool is now distributed free of charge to all kinds of users (including commercial companies). Version 3.0 was released in May 1996. It has three integrated parts:
Design/CPN supports CPN models with complex data types (color sets) and complex data manipulations (arc expressions and guards) - both specified in the functional programming language Standard ML. The package also supports hierarchical CP-nets (i.e., net models that consist of a set of separate modules with well-defined interfaces).
From what I have observed, CP-nets are a good tool to simulate and verify software or hardware projects. But according to one of the Design/CPN introduction articles on the web, Design/CPN could do around 1,000 message broadcasts per second, which compared to NeatTools' 1,800,000 messages per second is around 1,800 times slower. As a simulation tool, it is OK to have a speed at this level. For real-time applications, Design/CPN may not be the proper choice. The key difference is in the broadcast model itself not implementation. CP-nets use asynchronous message broadcast between modules as the default. It could avoid the cyclic deadlock problem and be much like real world conditions. But in the mean time, it decreases the performance a lot. NeatTools uses synchronous message broadcasting and simplifies the broadcast into a direct function call. Also, it uses a simple mechanism to avoid the cyclic deadlock problem without a great deal of overhead.
In "An Introduction to the Practical Use of Colored Petri Nets," by Kurt Jensen, [19], he gives the following description:
CP-nets have an explicit description of both states and actions. This is in contrast to most system description languages which describe either the states or the actions -- but not both. Using CP-nets, the reader may easily change the point of focus from states to actions, or vice versa.
In my opinion, actions and states could all be expressed and manipulated by message events, because message events could stand for expressions of current states or actions. Actually, in NeatTools, I did implement the state as a module into NeatTools. The user could layout state machine visually and use it for complex state analysis. CP-net's decision to separate the modules into states and actions may have some advantage for theory analysis. But it could make the model itself become more complex and hard to understand for those users who have no formal theory analysis training.
The following material regarding UML is extracted from Rational's [21] web page at http://www.rational.com/ and OMG's [20] web page at http://www.omg.org/.
The Unified Modeling Language (UML) is a language for specifying, visualizing, constructing, and documenting the artifacts of software systems, as well as for business modeling and other non-software systems. UML represents a collection of the best engineering practices that have proven successful in the modeling of large and complex systems.
UML definition consists of the following documents:
UML uses OCL, defined separately in the Object Constraint Language Specification document.
The choice of what models and diagrams one creates has a profound influence on how a problem is attacked and how a corresponding solution is shaped. Abstraction, the focus on relevant details while ignoring others, is a key to learning and communicating. Because of this :
In terms of the views of a model, UML defines the following graphical diagrams: use case diagram, class diagram, behavior diagrams, and implementation diagrams. These diagrams provide multiple perspectives of the system under analysis or development. The underlying model integrates these perspectives so that a self-consistent system can be analyzed and built.
UML, a visual modeling language, is not intended to be a visual programming language, in the sense of having all the necessary visual and semantic support to replace programming languages. UML is, however, a language for visualizing, specifying, constructing, and documenting the artifacts of a software-intensive system, but it draws the line as you move toward code. Some things, like complex branches and joins, are better expressed in a textual programming language. UML does have a tight mapping to a family of OO languages, so that the user can get the best of both worlds.
Since UML is not intended to be a visual programming language, it is very clear that it has no conflict with the NeatTools application and its design concepts as a visual programming environment. On the contrary, we can use UML to help in specifying, constructing, and documenting the artifacts of the development of the NeatTools system or module design. The Rational Rose, a visual modeling tool that allows developers to define and communicate a software architecture, could be used to speed up the development of future module design for NeatTools. In this sense, UML could indeed be very useful to NeatTools development.
The following material regarding Microsoft [8] Visual Series is extracted from Microsoft's web page at http://msdn.microsoft.com/visualj/, and http://msdn.microsoft.com/visualc/, and http://msdn.microsoft.com/vbasic/.
Microsoft Visual Series uses the same Developer Studio environment that comes with Microsoft's Visual C++. Microsoft has done a good job incorporating its existing tools into VJ++ and Visual Basic. For example, to create dialog boxes, developers use the same dialog box editor found in VC++'s Developer Studio.
Windows developers will like the COM integration in VC++, VB, and VJ++. COM, Microsoft's component object model, is the core of ActiveX, and developers can incorporate existing ActiveX controls and COM-based technologies into their VC++, VB and VJ++ applications.
Developers can also build their ActiveX controls for use with Visual Basic, VC++, and Delphi. This is a fairly complex process, but the final release of VJ++ is expected to include an ActiveX Wizard that will make it easier to convert applets into ActiveX controls.
Developers could use application (or applet in VJ++) Wizard to create a small application. After they respond to the Wizard's few simple questions, it creates a commented source-code skeleton (and an HTML file in VJ++) to host the application (or applet). Thus, a user could start from there without the need to start from scratch.
Basically, I would consider Microsoft's Visual Series as a textual programming environment with some visual tools (like dialog box editor), ActiveX Wizard, and application Wizard to help a programmer integrate their applications with resources, ActiveX components, or to create skeleton source-code, etc. Conceptually, this is not relevant to the visual programming environment that NeatTools provides.
The following material regarding JavaBeans is extracted from JavaSoft's [24] web page at http://www.javasoft.com/beans/index.html.
The goal of JavaBeans is to define a software component model for Java, so that third party ISVs can create and ship Java components that can be composed together into applications by end users.
What is a Java Bean? A Java Bean is a reusable software component that can be manipulated visually in a builder tool. The builder tools may include a web page builder, visual application builders, GUI layout builders, or event server application builders. Or it may simply be a document editor that includes some beans as part of a compound document.
Some JavaBeans components may be simple GUI elements such as buttons and sliders. Other JavaBeans components may be sophisticated visual software components such as database viewers, or data feeds. Some JavaBeans components may have no GUI appearance of their own, but may still be composed together visually using an application builder.
Individual JavaBeans components will vary in the functionality they support, but the typical unifying features that distinguish a JavaBeans component are:
While beans are primarily targeted at builder tools, they are also entirely usable by human programmers. All the key APIs such as events, properties, and persistence, have been designed to work well both for human programmers and for builder tools.
The linkage model in JavaBeans is naturally built on top of new Java 1.1 event broadcast mechanism and object class runtime information API. A bean programmer should follow proper design patterns to design interface method and property signature. So, the bean builder could study a bean's supported properties, events, and public methods through a low-level reflection mechanism or via BeanInfo class. In order to broadcast events between beans, bean builder first obtains the source bean, destination method, and event type information through its user interface. Later, it has to create a linkage class dynamically. The linkage class will register itself as an event listener to the source bean and also acts as a bridge between the event source and destination method. So, when the source bean broadcast an event, the destination method will be invoked through the linkage class. The linkage building process does not happen completely in run-time, because the dynamically created linkage class and other generated source files needs to be compiled before the application could be executed.
The most noticeable differences between NeatTools' module model and JavaBeans' component model are introspection process and linkage creation.
JavaBeans use a low-level reflection mechanism or BeanInfo class to study the methods supported by a target bean. In Java, the low-level reflection mechanism is already built in the system API. For example, the object.getClass method returns a class object, which describes the current object instance. Through the class object, we could walk the class hierarchy or determine the signature of a particular method. In C++, this kind of mechanism is almost impossible without an extensive implementation to embed the detailed run time class object information automatically. In NeatTools, the introspection process is not needed, because methods signature are fixed and inherited directly or indirectly from the abstract module base class. By deriving class from a generic data type class, methods with fixed signature could be used to broadcast events with different data types. Hence, the same functionality could be achieved.
In NeatTools, the linkage creation process is very simple and efficient compared to JavaBeans. The linkage data structure is fixed and it records the source module, source port number, destination module, and destination port number. No linkage class creation and compilation is involved during the linkage creation process. When a user requests to create a connection by mouse drag and drop, the NeatTools desktop just initializes a linkage data, fills in the information, and adds it into the linkage set. Unlike NeatTools, bean builder usually does not have a visual linkage layout representation. Instead, the linkage information between beans is accessed through the pop up menu or separate data entry form.
In general, JavaBeans' linkage and event broadcast model is very flexible and scalable. It takes advantage of some special features inside Java API, which is missing in C++, to assist the introspection process. In stead of using run time object class information, Object oriented programming technique was used to implement the module abstraction and event broadcast, which is simpler and more efficient. Since we decide to implement NeatTools in C++ and JavaBeans' linkage model is not suitable for language other than Java, NeatTools' module abstraction and event broadcast architecture is the reasonable implementation choice.
The following material regarding the LabView software package is extracted from National Instruments' [23] home page at http://www.natinst.com/.
LabView is a graphical programming development environment based on the G programming language for data acquisition and control, data analysis, and data presentation. LabView gives a user the flexibility of a powerful programming language without the associated difficulty and complexity because its graphical programming methodology is inherently intuitive to scientists and engineers.
With LabView, a user builds VIs (virtual instruments) instead of writing programs. Users can create front panel user interfaces, giving them interactive control of the software system. To specify the functionality, a user assembles block diagrams - a design notation for engineers and scientists.
LabView implements a patented data flow programming model, called G, that frees the user from the linear architecture of text-based languages. Because the execution order in LabView is determined by the flow of data between blocks, and not by sequential lines of text, users can create diagrams that have simultaneous operations. LabView is a multitasking and multithreaded system, running multiple execution threads and multiple VIs.
In many applications, execution speed is critical. LabView provides the compiler that generates optimized code with execution speeds comparable to compiled C programs. With the build-in Profiler, a user can analyze and optimize time-critical sections of code.
In the new version of LabView, the multithreading is built into VI, or LabView programs, thus it is not necessary for users to learn new programming techniques. In fact, the user can benefit from multithreading without even knowing what it is. However, for expert users who want to have specific control over threads, such as changing thread priorities, that flexibility is available in a straightforward dialog box option.
NeatTools and LabView are both visual programming environments. NeatTools is based on the abstract module model and LabView is based on the G programming language. The abstract module model tries to generalize and simplify the module and event broadcast model from a pure visual programming perspective, while the G programming language tries to have the graphical representation of traditional textual programming. For example, you can see the "while loop" and "for loop" structures in G programming language. In NeatTools, we do not define "while loop" or "for loop" structure in module specification. All modules are logically equal and they all derive from the same module base class. In one word, NeatTools users could use the Timer or ClockDivider modules to provide the same functionality. Timer and ClockDivider are just regular modules. Basically, a NeatTools user could design and use anything he likes to accomplish the desired tasks.
The graphical compiler in LabView provides optimized code with execution speeds comparable to compiled C programs. This indicates that the simulated version in LabView is slower and much bigger than the compiled version. In NeatTools, as I discussed in previous sections regarding NeatTools' performance, its data flow network is already comparable to compiled C/C++ programs. Also, NeatTools executables only include the core modules, thus the user could just add functionality by including the external module DLLs needed to meet the space constraints. Hence, the compiler is actually not needed in NeatTools.
Recently, the new LabView version started to support multithreading. The thread itself is associated directly in the VI. A user can change the thread's priority through a dialog box. I do not think this kind of add-on threading could provide a user all the power and flexibility of threading. In NeatTools, every module could start their own thread (or multi-threads) depending on the tasks it needs to perform. By using connections, several modules could share threads together, or modules could use internal threads for calculation intensive tasks, etc.
LabView has a very strong user group and history going back over ten years. NeatTools originated about 2 years ago. NeatTools has some technology advantages over LabView owing to its fairly new structure and design. I believe, with proper promotion, NeatTools could become popular in the educational market as well.
The following information regarding AVS/Express is extracted from the home page of Advanced Visual System Inc. [22] at http://www.avs.com/.
AVS/Express is a tool for developing data visualization applications. It enables users to explore data sets of any dimension or complexity using visualization capabilities.
AVS/Express offers the same benefits as other advanced client/server development tools. It provides a multi-platform, multi-OS development environment that allows broad-based application building. It enables developers to spend their time adding value to applications instead of trying to solve non-productive, costly system-level issues.
The AVS/Express Graphics Display Kit is a set of objects that contain the data structure and function to develop data-intensive, interactive graphics applications. It provides the following graphics-component technology essential for rendering and manipulating text, 2D images, and 2D and 3D geometric objects. These components can be reconfigured, customized and easily replaced to provide custom views for end-user applications.
The Data Visualization Kit contains objects, data structures and libraries needed to visualize and analyze data sets. The data model within this kit defines how data is represented, how functions access it, and how it is communicated between functions and processes. The AVS/Express data model could handle the data frequently found in data and graphics intensive applications, including image, volume, finite element, scattered and geometric data. The visualization components in this kit contain computation methods that are key to turning abstract data sets into graphics.
The Database Kit provides interfaces to the most popular SQL-based relational database management systems (RDBMS), including Oracle [15], Sybase [35] and Informix. In addition, the data kit contains an Open Database Connectivity (ODBC) interface. A library of configurable visual objects provides connections to one or more relational databases, and enables display of database tables and assembly of SQL expressions.
In AVS/Express, users construct applications as a connected, hierarchical network of objects, create and modify data structures with "drag-and-drop" ease, manipulate widgets directly to create GUI layouts, set up GUI callbacks through visual connections between widgets and application methods, examine application state and data with integrated debugging tools, and integrate external functions with encapsulated C, C++ and FORTRAN routines.
NeatTools is meant to be a generic fine-grained visual programming tool for developing human computer interfaces. NeatTools' focus is on how we could increase the information flow throughput between humans and computers. Thus, the key issues are on how we provide more information paths from humans to computers through sensors, devices, and broaden all kinds of feedback from computers to humans through graphics, audio, video, touchware, etc. It is possible that we could write all kinds of AVS/Express modules to provide the functionality specific to the NeatTools project. But, we never could change AVS/Express system itself to meet some special constraints and solve the problems specific to the NeatTools project. Due to different design concepts and implementation architectures, AVS kernel provided the centralized control over the inter-modular communication, and hence serialized parallel channels. Also, AVS uses IPC data-flow protocol to implement the channel event broadcast and data exchange, which limits AVS as a coarse grain visual programming tool. Thus, AVS could be very convenient for coarse grain integration such as manipulate and control the application flow of modular processes distributed in different hosts. When it comes to hundreds of modules connected by thousands of connections with high frequency event broadcast, what we need is an architecture that not only provides a high performance inter-modular communication, but also unleashes the power of well designed fine grained modules in the hands of a user. NeatTools fits into this kind of scenario in human computer interface very well and it provides flexible solutions as a general fine-grained visual-programming tool.
The design goal of NeatTools is to make it simple, object-oriented, network-ready, robust, secure, architecture neutral, portable, high-performance, multi-threaded, and dynamic.
The goal of building a system that could be visually programmed without a lot of formal training in computer science was achieved. At the SigGraph Conference 98', a very large booth in the SigKids area allowed children (around 10-12 years old) to freely use a full version of NeatTools interactively as an experimental tool. Most children could pick up the concepts of NeatTools in a very short time and started exploring on their own. One of them was definitely the best of the best. He picked up all the major ideas in a half-hour and started to design his own data-flow networks ranging from a "metal detector" to "mouse position finder." This proves that the NeatTools visual programming environment design is naturally simple.
I added several concepts to NeatTools, which are based on purely visual programming aspect instead of the traditional textual programming. For example, most visual programming languages like AVS/Express and LabView, have distinct modes for data-flow network editing and execution, which are quite naturally accepted in traditional textual programming. Because every textual program source code needs to be edited and compiled before it can execute. Also, from the design point of view, making editing and execution mode exclusive will make the system design task easier. The mixture between editing and execution mode will cause a lot of technical difficulties especially in a multi-threaded system. NeatTools provides non-stop execution. So users can layout, execute, and debug data-flow networks at the same time without being interrupted.
In module design, a programmer will first identify the module object, which will be used by the data-flow network design as the basic building block. Usually, this portion is most difficult because it will require broad knowledge of object-oriented programming and visual programming environment design. After the module object has been identified, the programmer could proceed to create a class which derived from the module base class or other module (when needed to inherit the common behavior). In terms of the process, creating a NeatTools module is very simple and straightforward.
Object-oriented design is very powerful because it facilitates clean definition of interfaces and makes it possible to provide reusable software ICs. By software ICs, I mean the design hiding the implementation and information and only providing the interface as the communication media to the outside world. So the designer could focus on the design and implementation of software ICs without additional concerns.
In NeatTools, each module was designed as a special object unit. The object unit defines the module's visual representation, connection channel, connectivity of each channel, attribute, etc. All the interactions between modules are handled by the module's base class and NeatTools system. So the designer can focus on each module as an independent unit and may improve the algorithms or debug inside the module.
NeatTools integrates an extensive library of TCP/IP protocol routines into the visual programming building blocks. For examples the SocketObj and ServerSocketObj modules can be dropped into the desktop and create a network connection by just making connections which is much easier than in a traditional textual programming environment. In the future, to complete the network functionality, we will add more modules that can handle HTTP, FTP, and other network related protocols.
Most dynamic languages like LISP, TCL and Smalltalk are often used for prototyping. One of the reasons for their success at this is that they are very robust: we don't have to worry about freeing or corrupting memory. In Java, the programmer can be relatively fearless about dealing with memory because they don't have to worry about it getting corrupted. In C/C++, the pointer and memory management are the most problematic issues. So how could we make the NeatTools become robust and still use C/C++? I believe proper usage of object oriented programming is the key issue to achieve this goal. By hiding the pointer and memory management inside the data members, constructor, destructor, and assignment operator methods; the interface of object class could be free of pointer and eliminate the need of complex memory management -- it becomes distributed into each object class implementation.
NeatTools is designed to be used in network/distributed environments. By using the Socket/ServerSocket modules, NeatTools running on different computers could collaborate with each other through data exchange and events. Recently, a lot of emphasis has been placed on security. To enable a secure visual-programming environment, the addition of authentication modules that are based on public-key encryption was planned, users could use the authentication module to encrypt or decrypt events and close the door on most tampering activities.
In the present computer market, application writers have to produce versions of their applications that are compatible with the IBM PC [36], Apple Macintosh [37], and other high-end workstations. With the PC market (through Windows/NT) diversifying into many CPU architectures, Apple moving off the 680x0 toward the PowerPC [37], and all the different brand named workstations based on different CPU architectures, production of software that runs on all platforms becomes nearly impossible.
In Java, the same version of the application runs on all platforms. But the Java bytecode relies on the Java runtime that allows the Java virtual machine to run the byte code. As demands come from all the different industries, Java runtime is constantly revised and becomes bigger and bigger. Because of that the browsers that carry Java virtual machine becomes huge, and they waste a lot of space when different Java runtime instances are needed by different browsers or Java development tool kits.
Some existing solutions, like the Win/U from Bristol Technology [38], provide biased implementations that favor a particular platform. For example, a lot of Microsoft Windows applications were written based on the Microsoft Foundation Class (MFC) [8], which is built on top of Win32 API. Win/U provides a layer that simulates the Win32 layer on the UNIX platform. So applications based on MFC could be compiled and run on the UNIX platform. This solution is based on the fact that lots of existing applications are already written for the Microsoft Windows platform. But it introduced unnecessary layer into the UNIX platform and decreases the application's performance.
NeatTools is built upon a Java Like Cross Platform API. In this API, it defines the basic language, networking, input/output, utility, and abstract window classes. It hides all platform dependent implementation details in the classes, provides a unified class interface to the applications that are based on this API and does not favor any of the platforms. By using this architecture neutral API, we only have one very thin layer between the application and the operating system it is running on. It guarantees that the application becomes cross-platform and will also have high performance.
Being architecture neutral is a big chunk of being portable, but there's more to it than that. All the platform dependent details are hiding in the object implementation. What the object interface provides is unified to all platforms. This is the classic usage of the object-oriented feature to build a clean portability boundary between the C/C++ POSIX subset, operating system, and applications.
The architecture neutral, portable, Java like cross-platform API did provide