Useful Tips
10/4/97
Where are the example programs?
The example programs can be found in the demos directory of the CAVERNsoft
distribution.
My 2 remote CAVERN programs don't seem to connect
properly.
If on startup one CAVERN program does not seem to be able to connect to
a remote CAVERN program running at a remote site, even though the 2 programs
seem to work locally between 2 different computers: it maybe because your
local and/or remote machine is not giving CAVERN its full hostname, but
instead an abbreviated name. E.g. A full hostname might be ivan.evl.uic.edu.
An abbreviated one would be ivan. You can tell if this is the case by typing:
hostname at the unix prompt. To solve this problem you may need to add
the following file to your home directory:
.resource_database
In it you need to place a declaration to identify the domain name of
your local machine. (You may also need to do something similar on your
remote machine). For example if your local machine is called ivan and the
full hostname is actually ivan.evl.uic.edu you need to add the following
line to the .resource_database file:
ivan domain=.evl.uic.edu
Can I run 2 CAVERNsoft programs on the same computer?
YES. Usually you do this if you wish to have one CAVERNsoft program connected
to another CAVERNsoft program that happens to be running on the same computer
(for the sake of convenience during debugging of your program.)
To do this each CAVERNsoft program must start on a different port.
There are 2 ways to do this. The quick and dirty way is to simply
set the CAVERN_PORT environment variable to the desired port.
For example:
setenv CAVERN_PORT 9000
Alternatively you can do it from within the CAVERN program.
Here is an example of initializing 2 CAVERN programs to run on 2 different
ports.
Program1:
CAVERN_initAttrib_c initAttr;
initAttr.setPort(7771);
personalIRB = CAVERNInit(&argc,&argv, &initAttr);
Program2:
CAVERN_initAttrib_c initAttr;
initAttr.setPort(7772);
personalIRB = CAVERNInit(&argc, &argv, &initAttr);
Program 1 will initialize CAVERN on port 7771 and program 2 on port 7772.
Now, for example, in order for program 1 to open a connection to program
2, program 1 must do the following:
CAVERN_irbChannel_c *aChannel = personalIRB->createChannel();
CAVERN_irbId_c remoteIRB;
// Insert your own hostname below.
remoteIRB.setAddress("ivan.evl.uic.edu");
// Program 2's port is specified here.
remoteIRB.setPort(7772);
aChannel->open(&remoteIRB, NULL, CAVERN_irbChannel::RELIABLE, &status);
How to take a UNIX file and insert it into a local CAVERNsoft
data store
By default CAVERNsoft will create a data store directory in the same directory
in which the CAVERNsoft-based program runs. This directory is usually called
CAVERN_DEFAULT_DB. To make a UNIX file a part of this database simply move
the file into the CAVERN_DEFAULT_DB directory and run a program called:
on the file. This program is located in the tools
directory of the CAVERNsoft distribution directory.
How to take a UNIX file and upload it into a remote
CAVERNsoft IRB.
If you wish to upload a UNIX file into a remote IRB (the remote IRB must
already be running for this to work) use the program called:
The syntax is:
dbdeliver remoteIP localFile remotePath remoteName
where:
-
remoteIP is the IP address of the remote IRB.
-
localFile is the local UNIX file you wish to deliver.
-
remotePath is the remote key's path in the IRB's key hierarchy in
which you wish to place the data. To specify a NULL path use "/"
-
remoteName is the remote key's name.
This program is located in the tools
directory of the CAVERNsoft distribution.
How to download data in a remote IRB to a local UNIX file.
If you wish to download the data stored in a key on a remote IRB to a local
UNIX file use the program called:
The syntax is:
dbget remoteIP remotePath remoteName localFileName
where:
-
remoteIP is the IP address of the remote IRB.
-
remotePath is the remote key's path in the IRB's key hierarchy in
which you wish to place the data. To specify a NULL path use "/"
-
remoteName is the remote key's name.
-
localFileName is the local UNIX file you wish to save the data in.
This program is located in the tools
directory of the CAVERNsoft distribution.
Debugging a CAVERNsoft program
It is difficult to debug CAVERNsoft programs with DBX since DBX on the
SGI's is not designed to work well with threaded programs. Unfortunately
printfs are your main salvation. CAVERNsoft provides a thread-safe printf
that you can use to print your own debug messages. Its prototype is:
void cvrnPrintf(char* format, ...);
How to use channels efficiently
As CAVERNsoft allows you to link any number of keys over any given channel
there may be some ambiguity over what is the optimum number of channels
to create and what is the optimum number of links to assign to each channel.
Also there is the issue of what kinds of channels are best for transporting
different kinds of data. Here are a few rules of thumb:
For transporting avatar tracker information a separate unreliable channel
should be assigned. This channel can host all the tracker traffic for the
avatars.
For information that conveys the state of entities in the virtual environment
use a separate reliable channel.
For sharing 3D geometries between clients etc, use yet another separate
channel. Do not simply combine the latter 2 channels together as large
amounts of geometry data can easily slow down smaller state data. By assigning
them to separate channels the transmission of the data is interlaced.
CAVERNsoft Fault Tolerance Provisions
CAVERNsoft was designed with persistence in mind and hence it makes every
effort to not crash or terminate a program when channels and links are
closed or broken. Instead it offers callbacks to user-specified functions
that can be used to alert the program of such events. The program can then
proceed to attempt to re-establish the channel. At the current time this
must be done manually by the user. In the future, CAVERNsoft will attempt
to re-establish connections automatically.
So currently when you receive an event that a channel has broken, simply
delete and create a new channel object, delete all the previous link objects
and then create all new links with the newly open channel. You do not have
to redefine the keys. Those will not be affected. Note however that we
can't protect you against your own accidental accesses to dangling memory
pointers. Those will surely kill your program however they should not adversely
affect the remote IRB programs to which your program may be connected.
Your disappearance will eventually be noticed and any resources that you
may have held subsequently recycled.
As part of CAVERNsoft's fault tolerance provisions, you may notice that
there are some status values returned from the various C++ member functions
that state that a link, channel, or key is no longer valid (or stale).
These statuses are provided so that you do not accidentally access dangling
pointers that point to deleted objects. Everytime you access any of CAVERNsoft's
object member functions it verifies that you are accessing a valid object
before it proceeds. This allows CAVERNsoft to dynamically tear down broken
connections and reclaim system resources without adversely affecting your
program.
To reclaim resources CAVERNsoft spawns off a separate garbage collection
thread which monitors your personal IRB every 10 seconds. Hence some times
when you disconnect from an IRB-based server and then reconnect too soon,
it may not allow you to re-create certain links because the old links have
not yet been purged from the system. After 10 seconds you can try again
and odds are, everything will be fine. Later we will provide a more responsive
garbage collection system.
CAVERNsoft and concurrency
CAVERNsoft is written with Nexus' threads package which are basically macro
definitions over pthreads. Hence if you need to, you may create your own
threads by using the standard pthreads or Nexus threads calls. If you have
never programmed with threads before it may be easier to use CAVERN's thread
classes (CAVERNplus_thread_c, CAVERNplus_mutex_c, CAVERNplus_condition_c).
These are classes that encapsulate the most basic functionality of Nexus/pthreads.
CAUTIONARY NOTE: You should exercise caution when working with fork()
as forking copies of threads can produce unpredicatable results. In general
the best policy is to perform all your forks before calling CAVERNInit().
Hence in the CAVE, CAVERNInit should be done after the CAVE has forked
off all its draw processes.
Miscellaneous References