|
|
|
Displaying Newsletter
|
Issue 17, 04 May 2002
|
|
|
|
|
Everything You Didn't Want to Know About the App Server
|
by DarkWyrm |
|
|
|
When I signed on with the OpenBeOS project,
I knew finding out how the app_server worked was going to be imperative to ensuring the rest of the Interface Kit actually got somewhere.
Little did I know just how much I was getting into, not that I'm complaining or anything.
As it turned out, there's a fair bit written about the app_server, but not anywhere in one spot.
Clearing up some misconceptions
Because the app_server is not very well-documented and seems to have its tendrils in just about anything,
there is a certain intimidating, mysterious aura around it.
The app_server is, believe it or not, pretty much a regular application which acts as a mediator for other applications
and consolidates a big mess into one spot.
It does not dwell in the kernel, and there are all sorts of things which R5's server has in place which are not well known.
It is *this* close to supporting more than one monitor.
George Hoffman, Be's app_server architect, just never got around to it, so it seems.
Perhaps understanding some structure of our own server will provide a little insight as to why this is so.
The Great Compromiser
As I mentioned above, the server acts as a mediator. Any application can access the graphics hardware.
Quite a few hoops have to be jumped through, but this is true and happens to be how the server prototypes can use a second video card.
Imagine the mess there'd be if every running application were attempting to write to the screen all at the same time.
Even with semaphores, it wouldn't be pretty. The only bigger mess would probably be the general Windows API, but I digress.
Knowing who actually needs the app_server better helps us understand how it works.
Below is an illustration of who actually needs to talk to the server:
___________________
______________ | | ______________
| | | | | |
| BWindow |--| BApplication |--| BView |
|______________| | | |______________|
\ |___________________| /
\ / | \ /
\ / | \ /
\ / | \ /
\/ | \/
/\ | /\
/ \ | / \
/ \ | / \
/ \ | / \
/ _\_______|_______/_ \
_________/____ | | ____\_________
| | | | | |
| Input Server |--| app_server |--| registrar |
|______________| | | |______________|
|___________________|
|
|
|
|
_______|_______
| |
| Video Card |
|_______________|
Hey, what's that "registrar" thingy?
More perceptive readers may have noticed a part in the diagram which has had pretty much no documentation or discussion: the registrar.
In fact, the only way you can even know that it is even running is by a ps command under the Terminal.
The registrar is the only server, app_server aside, which is not derived from a BApplication.
If the app_server crashes, the registrar will be the only program left.
It handles the MIME database, maintains a list of running applications, broadcasts system messages, does shutdown procedures, etc.
A Well-Oiled Machine
The figure below depicts the major parts of the server and which interacts with which.
_________________
| |
| Main Threads |
|_________________|
/ | \
/ | \
/ | \
/ ______|______ \ _____________
/ | | \ | |
/ | ServerApp | \| Graphics |
/ |_____________| /| Module |
/ | / |_____________|
/ | / /
_________/____ | / /
| | | / /
| Desktop | | / /
|______________| _______|____/__ /
\ | | /
\ | ServerWindow | /
\ |_______________| /
\ / /
\ / /
\ / /
\ / /
______\/_____ ___/__________
| | | |
| Layer |-----| Decorator |
|_____________| |______________|
Notice that Windows and Applications have a counterpart in the server.
Views, such as Buttons, Checkboxes, etc. have a counterpart in the Layer object.
Each has a distinct resemblance to the "regular" version, but with significant changes to learn how to play nice-nice with each other.
The Desktop takes care of handling workspaces and monitors.
Layers are organized into a tree for each workspace, and, in a roundabout way, handle drawing the screen.
The graphics module handles all the low-level stuff, like initializing the graphics driver,
knowing whether to use hardware-accelerated functions, etc. and accounts for a fair bit of the prototypes' flexibility.
There are three major threads:
- app_server
- Picasso
- Poller
app_server handles things like application startup.
Poller monitors for messages from the Input Server and moves the mouse cursor. Picasso, despite the name, does NOT handle redraw.
It actually monitors for hung and crashed applications and ensures that server resources are freed in such cases.
Decorators simply call the graphics module to draw around the main rectangle of a window and make things look pretty. ;)
What's this all mean for OpenBeOS?
The actual protocols between the various members of the app_server's little clique are classified under NDA,
so it is likely that no one save Mr. Hoffman knows the intricacies of the how they talk to each other.
Even if more people know, no one is allowed to say.
This just means that in order to install the app_server, it will be necessary to install OpenBeOS' Input Server, the Registrar,
and libbe.so as a unit. The down side of this is that we must wait for each one to be complete to be able to use them in a day-to-day situation,
but the up side is that there is a ton of functionality that goes with them.
|
|
|
Last week, I came across an interesting problem which I had not dealt with before.
Some people on IRC were discussing the testing of the new netkit, and had problems with the unimplemented libnet call _h_errnop(),
which supposedly returns the address of the errno variable for network funcs.
In fact, I think it calls _h_errno() defined somewhere else, but that's the idea.
Since we want this stuff to be thread-safe, errno shouldn't be clobbered by calls made by other threads.
In the headers (search in /boot/header for netdb.h),
this function is then used in a macro to virtualise the UNIX network equivalent of errno, with this line:
#define h_errno (*__h_errno())
But the point is: How can threads, which belong to the same address space, access something different at the same address ?
The answer is Thread Local Storage (TLS),
which is an API that provides a means to get a 32 bit value that can have a different value for each thread in a team.
It's a valuable thing to help in the porting of a UNIX mono-threaded application.
And it is also used in the system libraries, as we will see below.
There is even a page of the BeBook that describes this API
(check also the header file).
While I was searching for more information about this,
Google kindly pointed me to this page,
where you can find the source code to the BeOS implementation of TLS on R4
(which seems to have been integrated in libroot by now, with a bit of help from the kernel maybe,
since the intel implementation uses inlined asm that makes use of the fs segment register),
and find out that the API comes from MS-Windows (*grin*)...
Another trick
For the new libnet, we need to make use of this TLS API.
But however, there is another issue: How can you be sure make sure the calls to _h_errnop() will succeed,
since the TLS slot needs to be allocated before making use of it?
Well there might be more than one way of ensuring this, one of which would be to use atomic_add() like this:
status_t *_h_errnop(void)
{
static uint32 initdone = 0;
static volatile uint32 initcomplete = 0;
// make sure we don't test a cached register
// that we wouldn't be able to assign from another thread
if (!atomic_add(&initdone, 1)) {
h_errno_tls = tls_allocate();
atomic_add(&initcomplete, 1);
} else {
atomic_add(&init_done, -1);
while (!initcomplete)
// busy wait the first thread to pick up init did it totally
;
}
return tls_address(h_errno_tls);
}
This is a bit of overkill for such a tiny thing.
There is a nicer solution, one that is used by the current libroot also,
which involves hooking in the library loading mechanism, using standard elf libraries features.
Google told me this was described here
(dang it, at least a nice tutorial about shared libraries !!!).
The trick here is we will provide the _init() function to the compiler (to the linker really),
so it won't include the default one when creating the shared object. This special function gets called just when library has been loaded,
before anything else is called in it. As you will see below it makes everything much simpler :-)
The code
Here is just what I proposed to the people on IRC to add to the libnet code. Quite simple but still helpful.
#include <TLS.h>
#ifdef __cplusplus
extern "C" {
#endif
// the TLS id
static int32 h_errno_tls;
// This hook gets called upon dynamic library load
void _init()
{
h_errno_tls = tls_allocate();
// however it doesn't check for error...
}
// this one is called before the library gets unloaded
// (fini means ended in French)
void _fini()
{
}
// returns the pointer to the _h_errno status variable.
status_t *_h_errnop(void)
{
return tls_address(h_errno_tls);
}
#ifdef __cplusplus
}
#endif
|
|
|
One of the toughest parts about design of an operating system is that everyone is a critic.
When you set out to design some other application, say, an accounting application, you don't get a wide range of audience.
Someone once said "If your business plan says that your intended audience is everyone, change it.
The last business that tried that was Life magazine, and they went under." Unfortunately, with an operating system,
everyone kind of *is* our audience. We certainly have to appeal to geeks, without whom there are no apps.
Operating systems must appeal to users, or no one will download and/or buy those applications.
Today, though, I want to focus on Geeks. This has always been BeOS's strong base, and I believe it will be the primary base of users for R1.
Geeks are interested in Cool Technology and fast, easy, elegant programming. BeOS has always been a haven for Cool Technology.
Buzzword enabled, if you will. The programming model is somewhat ... difficult to adapt to, for those coming from more single threaded worlds,
but it is not that hard, once you get used to it. Look back at the newsletter article on making a server - pretty easy stuff.
Probably the most compelling technology to come out of computer science is pipes.
Huh? What? Pipes? Big deal. Really, I think that they are a big deal.
Pipes, for those not of the shell persuasion,
are the "|" characters that allow users of the shell to connect the output of one command to the input of another.
You can say things like "ls -la | grep myFile | lpr" to print the directory listing of "myFile".
This is compelling because it represents a different way of thinking.
The "Windows" way, if you will, is to make every application do every feature vaguely related to its purpose.
Word has a whole drawing program embedded into it so that you can make simple drawings. Excel has a graph maker.
Most Windows apps have simple file management ability built in.
Microsoft *requires* applications that are marked as Windows 95 compliant to offer the ability to email a document.
The ToolMakers way is to create great tools. Unix did this with tools like sed, awk, tr, etc.
These are tiny little tools that are very good at doing *ONE* thing.
The power is that you can hook them together (with pipes) and make a custom piece that does exactly what you want.
There are an infinite variety of tools that you can build (pretty easily) from standard Unix commands and pipes.
BeOS followed the ToolMaker way.
The C++ classes in the kits do not have to be there to meet the very technical definition of an operating system.
The kits are designed to be "nearly perfect" code written by others that you, the programmer, can take advantage of,
spring board off of, and produce great apps faster. BeOS is all about empowering the programmer.
Taking care of the common work so that you can do the uncommon. A slogan might be "No two programmers should ever write the same code".
Things like BAlerts, file requestors and Translators are good examples.
At the same time, the API is not an overgrown, bloated monster, either.
There is a good organization (kits) with a second tier organization of objects that makes finding the functionality that you want pretty easy.
Pieces are easily replaced, updated or changed.
One of the advantages that C++ brings to the table is that, with the data abstraction model chosen,
we can change the implementation and not change the API at all. If not for this, I think that OpenBeOS' work would be much more difficult.
Making tools is a fundamental part of what BeOS was about and what OpenBeOS is and will be about.
This is one of the key elements of R2's planning -- to create minimal (not bloated) new APIs to make the programmer's life easier.
Our mission is not to overwhelm you with a 20 foot poster of classes out of the box, but to make your life easier.
Some of the things that Be was working on included SSL and XML kits. These are excellent examples of common needs that developers have.
We make the tools to make your code easier.
|
|
|
|
|
|
|