In a previous column (Issue 1-13), I wrote about the various ways to send
data on the BeBox. In another column (Issue 1-4), I talked about the
dangers of race conditions and deadlocks, problems inherent in a
multithreaded OS. Now I'd like to bring these two topics together and
talk about the differences between posting messages through BLooper
's
PostMessage()
, and sending messages through
BMessenger
's SendMessage()
.
Posting a message is simple. Given a pointer to a BLooper
object, you
simply invoke PostMessage()
upon that object, passing a
BMessage
object
as an argument. A subtlety here is that this implies that you're holding
onto a pointer: Most programmers remember learning about the dangers of
pointers. Once you have a pointer to an object, you're tempted to do
other things with that pointer, like dereference it to access a data
member, or call some virtual function. Unless the code properly locks and
unlocks the object, either of these actions can lead to an immediate,
abrupt, and unpleasant suspension of further computation! (Check out the
"Be Newsletter," Issue 1-4, for more details.)
An alternative is to use BMessenger
's SendMessage()
function. Prior to
DR7, BMessenger
objects could only refer to BApplication
objects. In DR7,
a BMessenger
can refer to any BHandler
(in other words, any object that
can receive a message). This is an obvious boon to interapplication
communication and, interestingly enough, it also provides a new way to do
intra-application messaging. Instead of saving a pointer to a BHandler
,
you can create a BMessenger
that represents that object. Using
SendMessage()
to communicate with a BHandler
is completely safe. Plus,
there are no pointers involved so the temptations are minimized.
Now let's consider "safe locking": The safe locking of pointers is pretty
safe, but it does contain a hole. Suppose you have a pointer to a BWindow
and you call Lock()
. Lock()
will return FALSE
if the object no longer
exists. Now suppose just before calling Lock()
the BWindow
was destroyed
and a new BWindow
was created and, furthermore, suppose the new object is
allocated at the same memory location as the old object. In this case
Lock()
will return TRUE
.
The new window will be locked, even though, in
the semantics of the code, the old window was the target of the Lock()
call. What happens after this is anyone's guess.
With BMessenger
s this can't happen. A BMessenger
to an object is unique
for the life of the application. Memory addresses or pointers, on the
other hand, can be recycled. This makes using BMessenger
s and avoiding
pointers the safest way to go. The style of coding that I'm suggesting
might look something like this:
TMyApp
::TMyApp
() {TMyWindow
*w
= newTMyWindow
(...); // Instead of saving the window pointer, // you save a messenger to the window:fWindowM
=BMessenger
(w
);TMainView
*main_view
= newTMainView
(...); // Similarly for the view:fMainViewM
=BMessenger
(main_view
);w
->AddChild
(main_view
); ... }
In the example the application object uses the BMessenger
s to communicate
with the BWindow
and BView
.
If one of these objects (the BWindow
or
BView
) is deleted, the corresponding
BMessenger
will report an error the
next time it's used. In this way the holder of the messenger is safely
informed of its cohort's demise.
Another benefit of BMessenger
s is that they have built-in support for
replies. If you post a message to another object in your application,
there's no way that you can reply to that post. However, with every
SendMessage
comes an automatic return address. So for every message
that's sent, the receiver can do a SendReply()
. This can be a useful tool.
The downside to BMessenger
s is that they're more expensive than pointers:
A pointer is only 4 bytes, a BMessenger
is 16.
Also, PostMessage()
is
currently faster than SendMessage()
. And of course, sending a message is
significantly more expensive than simply accessing some data or calling a
function. If some set of objects is tightly coupled, then using
BMessenger
s might not be the right solution given the performance penalty.
Here's another programming tip that makes use of the messaging system. We've received some feedback indicating that our framework forces frequent subclassing in order to extend functionality. I'm not denying that the framework is so oriented, but there are some nice ways to avoid this problem. One example is when one window (let's call it the "document window") wants to open a dialog box asking the user for some input. Let's say this dialog has two views: An input text field and a "Do It" button.
At first glance it seems like you have to subclass BWindow
to process the
message posted by the "Do It" button. However, there's another way that
doesn't require any subclassing. The document window creates the dialog
window and adds the two views. The document then calls
BControl::SetTarget()
on the button, making itself the target of the
button. Even though the button lives in the dialog box, the message it
sends when clicked will go to the document window. Finally the dialog is
displayed and the document window goes on its merry way. Now, when the
user clicks the "Do It" button a message is sent to the document window.
From this message the document can get the button, dialog, and any other
information in the dialog. So once it extracts all necessary information,
it tells the dialog to close. End of story. You get a nice little dialog
without any subclassing (except, of course, for the document window
itself, which must be a subclass of BWindow
).
The SetTarget()
functions in BControl
,
BMenuItem
, and BListView
are very
useful—give them a try.
From the day he first read about the BeBox, Sean Allen, partner and programmer at Infant Software, was hooked. “I wrote to Be and asked if the $1,500 price tag was a typo—they said no. That price, that power, a built-in database, multithreaded, preemptive multitasking: Geek love!” Sean thought it was rather fitting that his BeBox arrived on Valentine's Day.
“With the right marketing, the BeBox can explode. I expect the machine to grab large chunks of market share over the next few years. It will gobble up the NeXT and Amiga camps, video and MIDI markets look ripe too, and it would make a killer DTP machine. Once people experience a BeBox and see the price, converts are easy.”
Not only does Sean see market potential for the BeBox, Infant Software, a four-person company that develops custom CGIs (Common Gateway Interfaces), databases, and free/shareware, is betting its future on Be: “We've decided to forgo our current platforms (Macintosh and UNIX) and be a Be-only company. We want to carve out our ground now and be there with one of the tractor application when the BeBox explodes.”
Sean and his partners are working on a modular suite of Web tools, which use the Be OS™ database to share information. Modules include a web server, a visitor tracker, an API for CGI-type work, and game modules. They plan to publish their messaging API so anyone can write a module that will work with their tools.
“We believe everybody has the right to the basic tools to make their computers useful, so we'll make our core tools available for free. We'll only charge for add-ons, like the visitor tracker.” They anticipate releasing the first tools in the first quarter of 1997, followed by additional modules, such as a software metering API, various distributed network applications (including games), and "agent" software.
Unlike other platforms he's developed for, Sean says, “I feel like I really have a chance to put my stamp on the Be OS—to help shape it, to help it grow. Developers really matter to Be, and that's a refreshing change.”
What's more, the BeBox is fun to program: The Be OS is 100 times easier to program than the Mac OS, 500 times easier than Windows, and 75 times easier than UNIX. That means shorter development times, better products, fewer bugs, and more time to dream, design, and build incredible software.
“I am, quite simply, in love with this machine.”
For more information on Infant Software, send e-mail to runt@inch.com.
We get many inquiries concerning employment opportunities at Be. With these inquiries come questions regarding the company's style and philosophy in such matters: What's it like working at a start-up such as Be? What sort of individuals are we looking for? We'll skip the usual meaningless platitudes: Honest, hard-working, creative, intelligent... (Of course we admire these qualities—but when was the last time you saw a resume that extolled the candidate's exemplary uncreativeness, sloth, and dishonesty?)
Let's proceed by comparison, then. In many large companies, the "product," at the work-a-day level, is broken down into "projects." Individual projects get sliced and diced into manageable components, and the components are parceled out to individuals according to their specialized training and experience. To coordinate the project components, you need project management; a premium is placed on management's ability to orchestrate, adjudicate, and focus the blindered energy that emanates from a line of wall-facing cubicles. A significant amount of time, money, and emotion are spent not in crafting the project itself, but in crafting the crafting of the project: In the careful placement of the walls between the compartments, and the minutiae of passing data over these walls. And this is just to mention the lowest level of management.
At Be, we hire people, not projects. Of course, we have certain tasks that we want to accomplish, and we have, at times, targeted prospective employees because of very specific talents. But, in general, we want individuals whose personalities and training make them comfortable with an environment where they're expected to grasp the whole concept, find a niche (or a canyon) that needs filling, and then start digging. We want to create an environment where the biggest chunk of any particular problem (be it engineering, marketing, manufacturing, finance...) resides inside a single human head. No turf wars, no miscommunication, and no layers of management.
This is an approach that only a small company can take—and if Be is going to win, we must take this approach in order to turn our size to our advantage. To the other guys, we look like ants; but from our perspective, they look like lumbering, graceless dinosaurs.
Another important trait that we expect in our employees is that they have a clear sense of the business purpose of their own actions. It's not enough that they think that they're providing a valuable service, or, worse, that they simply take marketing's word for it—they must know why it's important. In most cases, they should even know the names of the developers that their work will benefit most. At Be, "because marketing asked for it" is simply not an acceptable answer.
We want engineers to act upon their own estimation of what their clients will want, like, and pay for. We expect a similar understanding of the business across the entire organization. Now, what kind of Human Resources organization do we have to ensure this shared awareness of and commitment to the common purpose? None.
Most of us have observed the damage caused by addictive HR organizations. In the beginning, they relieve stress. Soon, they insinuate themselves into every aspect of corporate life and withdrawal pain becomes impossible to bear. Not unlike some welfare programs, HR becomes an end unto itself, a means of existence for a group of individuals whose sustenance depends upon their clients' problems. Decisions are laden with their intervention. Straight- forward processes are gummed up with their arbitration.
In a way, HR is a protection racket. A process must be put in place, a study must be made, a task force must be set up; thus management is protected and absolved of political responsibility, and HR gains power.
One of the benefits we offer at Be is that there's no HR and there won't be any. Personnel, yes. Forms, benefits, insurance, 401K—these are all healthy and regular paperwork movements. If we ever need HR work, we'll bring consultants to help. And then they'll leave. Just as we want our employees to own their work, so do we want an unobfuscated (and minimal) management to be fully responsible for its actions.
As for turnover: On a small sample size such as ours (population 30) statistics don't tell much. We've consistently lost two or three people every year. Our goal is to detect an ill-fit between the individual and the mission as early as possible. One of the benefits of early detection is a more amiable parting of the ways before frustration and bitterness set in. So far, it seems to have worked: One ex-engineer even treated the entire company to (good) pizza to help celebrate the BeBox intro. Another helped us recruit a new employee at a crucial juncture. But enough syrup; the bottom line is that we can only succeed if we continue to attract and hire a (small) number of mature, broad-minded, self-reliant individuals.
To be an efficient guerilla-type company, we need the kind of professionals who seek and will contribute to maintain such an environment. You can come from a large company, or be straight out of college: It doesn't really matter as long as you fit the environment you're in charge of creating.