Issue 2-40, October 8, 1997

Be Engineering Insights: Help! I Think I Might Have Bought an NC

By Erich Ringewald

Ever interested in broadening my understanding of the world around me, I recently checked into the web sites of IBM, Sun, and NCI (the subsidiary of Oracle which incorporated Navio and is dedicated to producing network computers under the benevolent and watchful eye of Larry Ellison) to have a look at their NC offerings.

An NC, as far as I can tell, is a computer designed to run a web browser, has a convincing Java implementation, can't be screwed up by the user, has no local storage, can be booted from a server and configured remotely by an all-knowing authority. Also, it should be cheap. And most people seem to think it would be a good idea if it did all this without any Microsoft software whatsoever, although that's mostly coming from manufacturers, not end users.

Some quick perusals of the web sites quickly yield a fairly traditional profile: a slim box with a non-Intel processor, no IDE, serial, parallel, or any peripheral connectors to speak of, an SVGA connector, keyboard and a mouse. Usually they're packaged in a handsome black design, and carry price tags around $700 (to start, monitor not included).

Of course, the value of these things is lower cost of ownership, and we're not just talking about purchase price, because we all know the real price comes from operational costs after the machine has been bought.

What I don't quite understand is the reason for a whole new computer to run this new software. I actually just bought a machine down the street (for which purpose I'll not disclose) which on closer inspection was a lot like the machine I just described. I saw it advertised in a not very elegant ad in Microtimes:

166 MHz Pentium MMX
1.44 MB Floppy
S3 Accelerated Graphics (2MB RAM)
Case/Power Supply

Price: $495.

It needs a monitor, but as it stands, it seemed like it would make a pretty powerful NC. But what it needs is software.

You see, if I forget about all that fancy NC hardware which people want to sell me, and focus on the software which might make the machine I mention above an easily and remotely configurable NC, I realize that the NC argument is and should be a software argument. The computer above, relieved from the worries of a previous life imposed by Windows '95, could remotely (DHCP/BootP) retrieve a nice, simple and fast OS designed for web based computing and be as cost-effective an NC as anything in a fancy box from a NC hardware manufacturer.

I guess it's just more fun to build your own hardware. Or maybe it's easier to disguise your real intent. Never underestimate the possible manifestations of that less talked about Freudian condition, "Bill Gates Envy."

To paraphrase an old saying, if you're a hardware company, everything looks like a hardware problem, even a software problem.

News From The Front

By William Adams

So, is a spinlock a good thing? Last week when I went over what might be a typical spinlock implementation, astute readers immediately pointed out the flaws in the implementation, which is a good thing. I'll get back to that.

Why would you bother trying to implement such things in the first place? Well, many times you might think system resources are limited and must be preserved at all cost, and that system overhead must be minimized wherever possible. This sometimes leads developers to try and optimize areas they believe to be piggish before looking at their own algorithms.

Why did I use spinlocks as an example? Because, there is often times an assumption by speed demons that any function that calls into the kernel must be slow, and therefore is subject to optimization. Spinlocks are often thought of as a mechanism that provides a faster fine grain locking mechanism than semaphores.

The BeOS is a awesome system to work with, and very efficient at many things. If you have experience with other operating systems that do multi-threading, you might think it would be piggish doing certain things.

Multithreading from the bottom to the top is truly our motto. This has been the case from the first Hobbit-based BeBox to the multi-Pentium systems we run on today. If there is one thing we do right and efficiently, it's dealing with multiple threads running on multiple CPUs. That includes scheduling, locking critical areas, managing interrupts, etc. I think the BeOS handles these features much better than many other operating systems I have used in recent history.

One feature of programming in a new environment is that there are not shelf loads of books of the "Undocumented" variety available yet. Although this form of book tends to be a bit tedious at times, they do offer one very useful insight. You can typically find out the things that the standard documentation didn't tell you. That is, patterns of usage, and boundary cases.

The BeOS documentation is wonderful in explaining how things work, and although we try to tell you every minute detail of using every function, we may not meet your needs in all instances. But, being the industrious, inquisitive undaunted programmer that you are, you've taken to reading through headers to supplement your knowledge as much as you can. Of course this process is not ideal and in certain areas you must deduce certain behavior, and sometimes those deductions can be wrong.

If you are interested in how to control a thread's access to critical data, or interested in controlling the order of processing that occurs in your application, you absolutely must read the documentation on semaphores, which can be found on your BeOS disk at:


If you are new to multithreaded programming, but you love to do experiments with programming, you might do some reading on semaphores and come to the conclusion that you can do better. Last week's sample code is a typical example of how you might try to implement it on your own.

For instance, if you're coming from the UNIX/Solaris/Linux world, you might have read "UNIX Systems for Modern Architectures" by Curt Schimel and come across section 9.2 Spin-Locks. It seems like a good idea. A fine grained locking mechanism that doesn't do too much other than waste CPU cycles. So you start to implement...

First you see that you need a test_and_set() function. It needs to flush caches and test and set a value in an atomic way. Using swap_atomic is what is advocated, so you go traipsing through the Be documentation looking for such a function. Not finding it directly, you start looking through headers and come across SupportDefs.h which has nifty things like atomic_add, atomic_and, write_32_swap, and the like. You take a gamble and come up with:

long test_and_set(volatile long *addr)
    long old_value;
    old_value = write_32_swap (addr, 1);
    if (old_value == 0)
        return 0;
    return 1;

It's close, but not quite. First, the write_32_swap() doesn't return a int, but your compiler doesn't complain, so you think you're safe.

The code that will call this function wants to do one thing. If the value is 0, then set it to one atomically and return the fact that it was 0. If it is 1, then return the fact that is a one.

The proper way of doing this is to use the atomic_or function. The atomic_xxx functions return the value that was in the variable before it was changed. Thus, if it were already a 0, it would return that and the current value would now be a 1. If it were a 1 to begin with, it will return that, and it will remain a 1. Ahhh... perfect.

long test_and_set(volatile long *addr)
    return atomic_or(addr, 1) ? 1 : 0;

But you're not done. There may be multiple CPUs in the system. Do you need to flush caches explicitly so that the other CPUs will know that the memory address changed? Do you even know how to get to the caches? Or did that happen for you automatically when you used the atomic_or function? And how costly is that spinning anyway?

In the BeOS, and any other system for that matter, the atomic_xxx operations are supposed to be guaranteed by the OS vendor to do all those things. Flush caches if necessary, and utilize the proper CPU instructions to achieve the desired action while disallowing other actions from interfering.

The critical method of the ASpinLock class is the Lock() method itself. It does the test_and_set forever until it actually gets a hold of the "lock." Is this a good thing to do in user space? Not necessarily. Since the BeOS is preemptive, your process is subject to rescheduling. That means the kernel gets involved and does some context switching, which is what you might have been trying to avoid in the first place.

int ASpinLock::Lock()
  while (test_and_set(&fLockStatus) == 1)
  return 0;

What does that leave you with? Back to the Benaphore? Yes, of course. A Benaphore as explained in a previous newsletter article is a very fast, efficient locking mechanism. It has been written, debugged, and presented for your usage in the documentation. It does a quick test on a atomic variable, and then uses a semaphore if necessary. Yes, you make a trip to the kernel, but you were probably headed there anyway.

Spinlocks are still a very useful locking mechanism, when implemented correctly and in the right places. If you are writing kernel device drivers, spinlocks are your best friend for protecting critical sections of code. You will find drivers/KernelExport.h provides you with the appropriate function calls for your device driver to handle spinlocks. The following header file comment gives you a good idea of proper spinlock usage:

  spinlocks. Note that acquire/release should be called with
  interrupts disabled.

That right there is your clue the spinlocks aren't quite as easy to implement as they seem.

In short, we do the work, so you don't have to. We speak to CPU vendors and find those special commands that make atomic actions work on 603, 604, x86 and other CPUs. They aren't always obvious, and usually require assembly coding. Fine grained locking is typically something you do in the kernel space; in user space, you're better off with the semaphore and Benaphore techniques.

Armed with a locking mechanism, what can you do? What good are they anyway? It seems they will just slow down your application.

Locking mechanisms are the most fundamentally useful techniques to use in a multi-threading, multi-CPU environment. If you are new to all this, then I would recommend some reading: "Object-Oriented Multithreading Using C++" by Cameron Hughes and Tracy Hughes (Wiley). This is not a book on doing multi-threaded programming on the BeOS, but it does cover many of the features, concepts and techniques common to any multithreading environment.

Major or Minor?

By Jean-Louis Gassée

This refers, of course, to our latest BeOS release, DR 9.1, also known as Preview Release 2 (or PR2). If we go by the conventional wisdom of numbering conventions, since the "1" numeral is on the right hand side of the decimal point, this is a minor release. We know how this very convention keeps being abused for obvious marketing reasons, or more tortuous contractual ones, but, in our case, we don't mind the label.

One could summarize PR2 as the product of things we shouldn't have done, bugs, or things we should have done, features that didn't quite make it into the original Preview Release (PR1), such as, but not limited to, better handling of HFS volumes or the 3D kit. This doesn't mean we're not happy and proud of this latest release, we feel indeed it is a nice ".1" release, but, on the other hand, we're just doing what we're supposed to do.

The Preview Release was a risky effort, entailing many internal changes in the foundation of our OS, the new 64-bit file system being the most visible one. As a result, we were late by about 6 months, not an uncommon occurrence, but not an excuse either. With this in mind, one piece of good news regarding PR2 is we're back on track, correcting problems and adding features on a more regular basis.

As always, what we think of our work matters only very little. The verdict is in the hands of developers and early adopters. With PR1, we saw the beginning of commercial BeOS applications being shipped and sold. We hope PR2 will make life easier for our partners and customers and will generate the most precious commodity in our business, positive word-of-mouth.

Speaking of which... We owe thanks to all developers and users who gave us feedback after PR1 came out. Their comments, positive and negative, have given us information and motivation for the improvements, visible or not, you'll find in PR2.

As usual, we're nervous when a new release comes out: Have we overlooked some obvious problem, are we causing incompatibility grief to our developers, in our eagerness to correct problems have we caused more damage? We'll know very soon and, if need be, we'll post updates on our site.

From now on, our focus is on the Intel release and on helping BeOS developers test that version as soon as practical in order to open the gates to this bigger market in the most effective fashion for all of us. This is a new set of challenges. We realize there is more to the Intel world than a new instruction set, but we look forward to the task nonetheless.

BeDevTalk Summary

BeDevTalk is an unmonitored discussion group in which technical information is shared by Be developers and interested parties. In this column, we summarize some of the active threads, listed by their subject lines as they appear, verbatim, in the mail.

To subscribe to BeDevTalk, visit the mailing list page on our web site:


Subject: Searching for an example for fs_create_index()

The initial request, as described by the Subject line, led to a discussion of the nature of attributes, the BFS Btree, and the speed of the file system. Be's Dominic Giampaolo ran some tests that stressed the file system's ability to update its indices, posted his findings, pointed out the system's shortcomings, and vowed to improve the situation. Other listeners called in with their own benchmarks and a few questions:

  • Since it's not possible to have "invisible" files, what should you do to protect attribute-only files from being deleted by a curious user?

    A number of listeners suggested that attribute-only files sounds like a database—and the BFS isn't meant for industrial-strength database support.

  • Will multi-user support change the way attributes are stored/protected/accessed?

    Some listeners objected to the practice of storing user-info (configurations, protections, etc.) as attributes of a file. The popular opinion is that files should be protected by uid/gid and the permission flags—files shouldn't be selectively shared based on the value of an attribute.

  • How many files within a single directory are "too many"?

    Some difference of opinion, here. There was some consensus that once you get in the 10,000 file range, you'll start to lose performance. But is it the file system or the Tracker that's the culprit? It was offered that displaying (and Node Monitoring) 10k files takes a significant bite—but, then again, a window that contains that many files isn't a great user experience at any speed.

Subject: Networking woes

AKA: restarting networking
  1. Do you need a hub to get a BeBox to talk to another computer?

    Nope, (says Stephen Beaulieu), all you need is a "cross-over cable." Want more info? Ken Causey pointed out this URL:

  2. How do you restart networking programmatically?

    The simple answer: You can't.

    A longer discussion ensued: Is there any reason to restart networking? It would be better if changes to network settings were propagated as they occur, rather than dropping connections and demanding that the net daemon be re-launched. It was agreed that while the current implementation isn't ideal, it's considerably nicer than demanding a reboot. Or is it? Osma Ahvenlampi says...

    "For a server, it's the same thing. Who cares if the machine [is rebooting] or not, if all the network connections go down, it's down."

Subject: libraries and source code

An old friend re-visited: Should a developer use a library that doesn't include source code? To a library developer (as is Jeff Abrahamson, who re-started the ball rolling), the issues are these:

  • If the source code is included, piracy is more likely.

  • If you don't include source, developers are less likely to use the library for fear of not getting adequate support (they'd like to be able to patch the code themselves).

Mr. Abrahamson continued with a proposal:

Does it suffice to have the *option* of purchasing the full source code for, say, twenty times the cost of the library... You don't need to buy it unless you have a problem, but if you do you can always spend the money to buy the source.

Ed Musgrove modified the "no source, no service" attitude:

We developers are interested in debugging—we do not need the source code for any other reason. A sophisticated solution might be to create a #ifdef DEBUG block and ship two versions of the library.

This was seconded by a number of entrants. An additional thought, that all library developers should place their source code in escrow in anticipation of belly up day, was also heartily cheered.

Jon Watte didn't disagree, but that didn't stop him:

...anything starting with two underscores is the SOLE property of the compiler vendor. You should not use it. It is not ANSI, it is not portable, with the exception of the __cplusplus constant (and other well-defined constants such as __STDC__)


...#ifdef is a left-over from the first version of K&R C that should not be used in modern production code, because it is inflexible and there is a better variant: #if defined(x)

This led to another topic: Should bad code be forced to crash? Two schools:

  • It's bad for the user if a program crashes. Better to give the app the benefit of the doubt (and the user a more pleasant experience) by not assert()ing argument formats and types.

  • It's bad to write sloppy code. And sloppy programmers are probably also sloppy testers. We should force the bums to eat their own code.

Subject: Compatibility vs. "The Right Way"

Is binary and API compatibility worth maintaining at the cost of lost improvements and features? Relatedly, does the impending Intel release have anything to be compatible with?

A number of listeners phoned in to say that breaking the API between PPC and Intel is not a good thing—they may not affect each other directly, but maintaining two sets of API is never desirable.

This led to the broader question of cross-compiles and testing: Should a developer cross-compile to a platform that isn't sitting on the other side of the room? Some folks think that "absentee" development is prone to bugs. But Bill Morris has a slightly different attitude:

Strangely enough I code Java on the Mac using CodeWarrior and Netscape 3.0 just to avoid having to do that [test on each platform]. The basic theory is that since Java on Netscape 3.0 for Mac is absolutely horrible if it works there it will work better everywhere else...

One of our more frequent contributors suggested that a cross-compile panacea is a pipe dream. Another frequent contributor objected, citing a known, if defunct, example. Fur flew a bit.

Subject: BeOS security

A follow-up to the recent "BeOS as a Server" thread. This week: Security. If, as some people hold, the BeOS is to be a credible server OS, it needs to have tighter security. Thoughts?

Encryption schemes, security authentication methods and levels, network protocols, user authorization... There was no end to the topics. Michael Crawford lightened this acronymically-inclined thread with a story about Navy security:

One thing they had to do was install a power line filter because the inspector thought that spies could read the data off the power line from across the harbor. They found a filter in a surplus yard that my dad described as costing tens of thousands of dollars when new... Another thing they do in secure installations—you _can_ use a network, but you have to ensure no one taps into the net. The way they do that is to run the wire through a pressurized pipe. If someone drills into the pipe, then the pressure loss sets off alarms.

Creative Commons License
Legal Notice
This work is licensed under a Creative Commons Attribution-Non commercial-No Derivative Works 3.0 License.