|
24oct09
«
threat models
For fiction context, see part1, part2, and part3: coffee arena « Ollie smiled warmly at Sister Judy after she ordered a double espresso. Partly this was because, in his mind, Sister Judy went through a glorious martial arts move, her black nun's habit flowing like a fighting robe in Crouching Tiger, Hidden Dragon. In the middle of her battle cry, arms held akimbo in a classic challenge pose, Ollie's view of Judy froze and became a black and white drawing captioned in large letters like a movie poster: "Sister Judy, as The Equalizer." Ollie did something like this with most of his patrons, giving him a reputation for big smiles after meeting new customers is his coffee shop, Vintage Season, because Ollie appreciated how funny normal people looked in fighting mode. Apparently Ollie also sometimes made small sound effects under his breath like Bruce Lee when he saw himself challenging in reply: "Oliver, as The Proprietor." Once in a while, one of his baristas echoed Ollie's Kung Fu squawks, so he must do it out loud. His martial arts view of the world had been going on for years, so now Ollie worshipped Quentin Tarantino every time something like this happened in one of his movies. Tarantino's Inglorious Bastards had him in stitches. Ollie's buddy Deck (typically "Deck, as The Bladerunner") hadn't liked that one, so Ollie had imagined Quentin Tarantino appearing in person at Vintage Season to lift Deck bodily in the air to break Deck's spine over one knee: "Quentin, as The Auteur." Ollie was most proud of seeing patron couples reveal their real, inner selves in movie combat mode, like when Janet smashed Dan's face into the counter with an explosion of cookies and coffee cup holders, snarling ferociously: "Janet, as The Sadistic Bitch." But that wasn't the only reason Ollie smiled at Judy: he also kinda had the hots for her, because he thought she was beautiful. This bothered him even though he knew Judy wasn't really a nun. Some combination of psychological damage and sense of humor made Judy wear the nun's habit, so Ollie had only seen her face and hands. He actually dreamed about her face now. But he was having trouble asking Judy out—a woman one of his regulars called "a freakin nutjob." (This was one of Wil's coworkers, who Ollie cast this way: "Ulf, as The Asshole.") He almost had his nerve worked up, because Judy was much better now, never pacing in the tiny park next door anymore, ranting quickly and steadily, with one hand moving close to her face in a frantic orbit like a bird trying to escape a cage. Two months ago Judy scared Ollie's other patrons one day, excitedly talking to herself about Miss Morlock's scavenger hunt—some kind of newspaper contest with weird rules—until Ollie decided to get some help. But instead of calling the police, Ollie asked Finch if there was anything she could do, as a favor to a friend, though Ollie wasn't sure why he felt Finch might be a help. After studying Ollie thoughtfully, then looking at Judy's rapid perambulation around a small table, Finch told him yes and approached Judy almost gently, even taking off her sunglasses before speaking in the privacy of the park. The change in Judy's attitude and posture was amazing when Judy looked into Finch's face, freezing in her tracks and relaxing completely, with a calm and rational look—other than fixating on Finch. The two of them talked for ten whole minutes, sitting face to face, alone in the park; Ollie didn't have time to watch the whole time, but it looked like an emotional conversation between old friends. Then Finch put on her sunglasses and left. Judy was totally changed, and came in to talk with Ollie, tentatively, like a coma victim awakening after a dozen years, wondering exactly where she was. Among other things, Judy seemed dramatically smarter, with a balanced and joyful emotional outlook. Since then, Ollie looked forward to seeing Sister Judy every day, because now Judy was one of the happiest people Ollie had ever met, with an intelligent and graceful manner. Ollie was addicted to making Judy smile, because it made him feel better than anything he could remember. But he wasn't sure where he was going with this. Judy showed him parts of the scavenger hunt she had solved so far, and Ollie made suggestions about what to try next, helping Judy interpret cryptic clues Miss Morlock added to every column for participants in her popular alternate reality game. For example, tonight Judy staked out another patron she called Mr. Steed—a nicely suited faux british agent wearing a bowler and carrying an umbrella—who Judy pretended was spying on Wil Munny's apartment across the street. (So far, Ollie had cast and recast Wil several times, depending on how Wil seemed, including parts as The Underdog, The Saint, and The Trickster. Besides Wil's friend Zé, and Finch, nobody else got as many parts.) Judy even went so far as to ask Mr. Steed, "Where's Mrs. Peel?" with a knowing look. Ollie guessed Mr. Steed might be in Morlock's game, but Steed looked at Judy like she was a lunatic, partly because Judy kept relaying remarks made by her "partner," Sam. For example, earlier Judy pointed at Sam and told Mr. Steed, "Sam says your hat looks pretty silly." Then Judy winked at Ollie when Mr. Steed looked away. Sam was a present from Finch: a lifesized cardboard cutout of a man in an informal jacket and tie, with curly dark hair and a mustache. Sam was actually a cardboard poster of a 70's television character named Gabe Kotter, played by an actor named Gabe Kaplan; why Judy insisted on Sam instead of Gabe, Ollie barely understood: Judy just said Sam wasn't an angel. Sam's eyes were some kind of holographic material, so his eyes followed you around the room; Sam's tie was also real, as well as a tie pin and a pair of cufflinks in his sleeves. Judy carried Sam around under her arm and folded him to sit at tables with her, and everywhere they went, Sam had something slightly rude to say to everyone, which Judy said for him since Sam was shy, she explained. It seemed Sam was made of something more durable than cardboard, because Sam never wore out, or became frayed despite constant use. Regulars at Vintage Season now said hi to Sam and asked how he was doing. When Ollie asked Judy if she, um, thought Sam was real, she punched him in the arm (just like Finch) and said, "Of course not," with a beautiful smile while searching Ollie's eyes. Her explanation had two parts. First, Judy got pleasure out of folks thinking she was still crazy; she couldn't go back to normal peer pressure, which was obnoxious. And second, one of her scavenger hunt tasks involved keeping Sam in the game. So it was an ideal match. Besides, as Judy explained, "Everyone needs a sidekick." Getting in the spirit, Ollie imagined Sam coming alive now and then, striking a Kung Fu pose before freezing in a movie poster reading, "Sam, as The Sidekick." Ollie considered getting a lifesize cardboard cutout of Electra, so they could double date when Ollie got the nerve to ask. But Sam wasn't bullet proof. Ollie would see this demonstrated later this evening, when Sam took a bullet right between the eyes, leaving a perfect circle punched through his head above his nose. advance warning « Ollie's first signs of trouble came at the same moment: his phone buzzed a new text message received, and Sister Judy distinctly said, "Oh, shit," in a surprised tone of voice. Ollie followed Judy's line of sight while raising his phone to read the message. Two slightly odd men approached the door of Vintage Season; the taller one looked all around carefully as if covering the other's moves. Both men had blonde, nearly white hair, and looked like brothers with ninety percent of their genes in common. The text message was from Finch: "I see them. Don't resist. Avoid the gun under your counter. I'll pay for damages. If I appear, drop to the floor and stay there. It might take an hour. Have patience. Do NOT touch a gun. Erase this message." Ollie read it twice in surprise before deleting. Finch only sent text messages to order coffee before. The first blonde man coming through the door had a smile Ollie didn't like; the second didn't smile at all. 21oct09
«
invested positions
kid grooming « I was never called smart in my family, which probably helped me, indirectly. My father hardly noticed any of us. I was just the second son. The first son, my older brother, was a chip off the old block taken around to meet folks. When I was ignored, I didn't notice much, because all five of us were ignored. My parents were completely indifferent to our grades. Lots of A's just got a "that's nice" comment. Would they be interested in paying money for good grades? No sirree. I pay my two sons for good grades: more for A's — barely at all for B's (to avoid making a B worthless if not an A). To make more A's significantly better, each A pays more than the last, in an arithmetic progression. Currently the scale is 8, 16, 24, and 32 dollars per A for the first four A's. So the next A is worth a lot more. And this year, to motivate them to pursue math over any other subject, I pay more for grades in math: $150 for an A, half that for a B, and half again for a C. Granted, this isn't much money, but to them it is. Left to themselves, I don't think they'd do as much work. When I hit high school, I started getting straight A's in all classes, every quarter. By the end of 9th grade, I had a perfect record. This was basically an accident since I wasn't trying, beyond doing work on time. After that I tried to get A's on purpose, because a perfect record was amusing. But basically I still didn't work to get all A's. I didn't need to study until college—a mild shock. When I remind my sons I got all A's in high school, they say, "Yeah, we remember," in a tone implying I made it impossible for them to excel in contrast. So indirectly my sons get a message they're dumber than me, but I make this message as weak as possible. (I don't have an attitude or opinion about intelligence as a way to rank people, which matches the view of my parents.) I was never called smart in school because I was obviously dumb socially, to an extent most folks were shocked when test scores came back. (I got the highest score in all topics about 90% of the time before college.) I was so dumb socially, I was unaware of how dumb I was. I struck most folks as an idiot savant, of sorts. I heard this at least dozens of times: "I guess you must be smart, after all." This with raised eyebrows and a thoughtful look. (How often did I imply you were an idiot? Or did you just miss all that?) Anyway, since I didn't have a position to defend, I had no need to protect a self image as smart, because I didn't have one. I merely scored well, and that didn't seem to matter to anyone but teachers. My teachers brought me extracurricular puzzles, to see if I'd do anything interesting, like solve them instantly—but I never did. My secret was too basic to be interesting: my grasp of anything they taught was excellent, which is dull. Maybe if I'd done less well, I'd have been praised more: as encouragement. As it was, I never had praise. In fact, almost the opposite happened: any positive feedback might go to my head, so a vacuum of feedback was safer. That had an unintended effect that should be obvious: I was never trained to crave praise, so I don't care. I perceive all praise as nuisance level manipulation. I see many people are vaguely addicted to a positive self image, however this can be done, even if it involves creative world views. After high school, my folks showed me the door and told me, "Good luck storming the castle," and shoved me down the front steps. I might have been dim socially, but I took this to mean my having done well in school was an affront making my father look dumb, and this was another little test he had for me. 19oct09
«
hero worshippers
time shifting « Doing multiple characters at once in a scene is hard in linear text, because you can't do service to many at once without distorting form and convention. So it's slightly easier to short shrift one or two characters, then time shift their view to a later or earlier point. Obviously later has the benefit of earlier context. Yesterday's piece accrued a debt to Eli's view, because he was mostly ignored. So he'll get a small followup later, focusing on his concerns; you always have to move the plot, no matter what, so Eli's thoughts lead immediately to casual remarks eliciting plot movement from Finch, since she's waiting for convenient cues. Why do I tell you this? So you see a bit of deliberate mechanics. Not everything is like this, though. Almost the same amount is spontaneous ideas I have one sentence before I write them. And larger than either of those is structural stuff based on two orthogonal factors: what characters want, and specific tactical things I know they'll do. I have a list of issues I expect them to try to pursue in a certain order. And I have a list of remarks I hope I can get them to say. (If I can't, they stay useful as subtext, by guiding later related behavior.) Yes, I want you to write some fiction. pipes under fibers « It took me a day to chase down a bug in code for buffered logging via worker threads, instead of doing individual unbuffered writes to stdout immediately. I added logging by the thread pool for two reasons: to work through things that should work, and to avoid greatly slowing future test runs when logging is enabled. What was the odd behavior I saw? The first flushed buffer appeared multiple times. I looked down all the wrong avenues first. Everything was right except for code run by a worker thread to write the buffer, which I had written to cope with both EAGAIN and EINTR. But in the process, I accidentally made it loop on success, too. Kinda dumb, really. But it was easy to fix. Now I'm doing pipes because I need to transform streams as they flow through asynchronously. I could model pipes using mutexes and condition variables, but that would be a bit clunky, and it seemed easier to do it directly: I can do pipes as a primitive feature of the lane-based fiber runtime, just like mutexes and condition variables. So the reader of a pipe can block directly on the pipe, putting that lane in a queue inside the pipe. (To express "being blocked" it's most consistent to have a blocked lane always in a queue; then later I can always cancel a wait, no matter where a lane is blocked, by removing it from the containing queue.) Now the good part about doing pipes using lane-based fibers is this: the code looks really simple. The lane model works really well. What might be conceptually complex—or difficult to code—is going to be simple and clear. Yay, I see leverage. If I write more about lanes later, this example makes good material to explain how actual api works. Here's how an async call works. (See old lane posts for definitions.) To call some method Bar::foo(...), you add two new arguments and change the return value into a yaw integer Y, like this: Y Bar::foo(B* nub, Kn* cb, ...)
Where nub B is the runtime context when the trampoline calls this method, and knot Kn is a continuation callback cb is a pair (W, F) to receive the reply with foo's original return value passed that way. For example, if you read from a pipe, and it already contains the bytes, you can put callback cb in the out-arc immediately, so the trampoline calls cb directly. Otherwise you make a new frame representing the need to perform this read, then block on the pipe and return yield as yaw integer Y. Later when the pipe gets bytes to satisfy the read, the blocked lane becomes ready again, reads the bytes, then finally calls callback cb. This strongly resembles the same sort of async callback model used for lots of things. But instead of being ad-hoc, it's an organized thread model using fibers. So you can apply conventional thinking and debugging tactics, instead of one-off hacks. 18oct09
«
real or memorex
part three « Here's a little more new fiction. For context, see part1, part2, and part3, in that order. Those three new pages split up the old contents of the fiction page, so you needn't download a long page to see new parts. The newest part three begins with Wil's pig fable, followed by a continuation of part two. flash gordon « "There's not much time," Finch warned Wil. "So forgive my haste: we might be interrupted before we cover enough ground. Don't let me rush you, but I don't have time to be subtle. So, I have better guns. Do you want them?" "Sure, what the heck," Wil made an easy gesture, glancing at Eli who slowly ran through the controls Wil showed him on his pistol. Eli aimed at the ground with exaggerated care. Wil continued, "But without training, how am I going to be more help than hindrance? I'm not a gun prodigy." "Not yet anyway," Finch half-smiled. "I wanted to talk to you about that. I think we need to take a shortcut. Can you guess what it is?" "Here we go," Flywheel's disembodied head observed. "Why do you always have these convenient crises to bend the rules? Pure chance?" Finch curtly asked Flywheel, "Did Aleph or Mick get back to you yet? Patch them through as soon as you can. Aleph calls on open lines, so don't say anything interesting. I assume Mick still has a full kit?" Flywheel looked chastened. "I'll be right back," he said, then his image froze and lost a bit of depth as X's appeared over his eyes to show he was offline. Zé returned from Wil's bedroom with his nano armor mostly assembled, asking Eli how to finish loose ends. Now everyone but Finch and Ulf wore nano armor. Wil suspected Finch didn't need heavy armor. Ulf tossed on the coach, still asleep; where was Ulf's armor? Wil didn't have time to think about it. "What about snipers?" Eli asked Finch while fixing a panel of Zé's armor in place. Eli glanced significantly at Wil's windows. "Covered," Finch said simply, drawing a weird looking handgun from inside her coat. "I'm watching several feeds in strategic spots, and automatic systems watch others. I see movement, but no one's that stupid." "That's an awesome looking gun," Zé enthused. "Buck Rogers from the 25th century? If that's an actual raygun, wouldn't that be, uh, sorta illegal? Assuming someone cares about anachronistic weapons." "Where do I put that?" Wil looked down at his costume. "I'm running out of places to put stuff; I need a utility belt or something." "Illegal to Chthon agents, yes," Finch agreed. "You'd be surprised by how much freedom I have, even when I'm not supposed to have any." "Finch, are you doing anything illegal?" Wil asked quickly. "To what authority?" Finch shot back with raised eyebrows. Finch held out the gun and quickly ran through a series of control movements. "See how these resemble your old gun? Same cues in your glasses?" "To, uh, authorities in Thule, I guess," Wil replied, reaching for the gun. It's curves were exotic and beautiful. Wil had trouble grasping the power scale shown by his glasses. "Should I hold this in reserve?" "Good idea," Finch nodded. "I want you to hold back as much as possible. Don't show anyone what you can do. And don't explain. I hope you haven't told Zé and Eli how your ability works? The longer other agents take to figure it out, the better. Don't tell Aleph or Mick, either, and especially not Flywheel." Flywheel's image came suddenly back to life. "Don't tell me what?" he asked with a smile belying a bit of petulance. Then his eyes fell on the gun Wil was holding and grew large. "Holy Khronos, he gets one of those?" "Yes, and it's going to get worse yet," Finch replied. "So please keep your hysterical reactions in check. Are you listening?" "What are you doing?" Flywheel looked carefully at Finch. Finch laughed and smiled while explaining, "I was just telling Wil we need to be vague when we talk about his talent—to be safe, because we want Wil to make it to next week. Right, Wil?" Zé and Eli looked at Wil like he might catch on fire. "Dude," Eli said. "You can't fly or anything, can you?" Then to Zé: "That would be awesome." "Okay," Wil agreed nervously. "So, can I do it without giving it away? Won't someone figure it out? How do I be careful?" "You can use it any time," Finch allowed. "But you need to make your results look like an accident, as much as possible. Then observers can write it off to luck a good long while, before it gets implausible." Flywheel's eyes bulged, apparently dying to ask questions. Finally he essayed a remark: "Caught yourself a big fish there, Finch?" "Two of them," Finch glanced at Zé, who shrugged. "Now I'm going to outfit Wil with more tools, and I don't want you to bug me till you get Mick and Aleph. Don't ask how Wil plans to use the blades." "He gets blades, too?" Flywheel said weakly, closing his eyes. mayhem « "That's right," Finch nodded. "A man's naked in certain places and times without a sword. Relax, Wil, this will be fun." "Here's Mick," Flywheel announced. "I'll put him on." Flywheel's image went green and wireframe before disappearing. Then a new wirefame head appeared before resolving into a lifelike head of another man floating over the table, who seemed a bit familiar to Wil. Zé placed the resemblance immediately: he looked a bit like Fred Ward in Tremors from 1990, and a bit like Richard Geere in a 1997 remake of The Jackal, with a nice haircut and whiskers trimmed short around mouth and chin—about thirty-fiveish and well kept. But most importantly, he was one of the Hells Angels next to Finch in the photo from Woodstock in 1969. He seemed younger now. Mick's image moved, his eyes first taking in everyone present very rapidly, before fixing on Finch. He nodded and smiled rakishly. "I need a mayhem expert, Mick," Finch told him. "Why aren't you here yet? Did you get lost?" She ended with a teasing tone. "Nice to see you too, Finch," Mick started agreeably. His spoke with precise articulation and sounded—intelligent but distant? Then he became critical: "Do you have any idea what's going on out here? I see serious looking folk nearby. I don't walk into situations blind. And I don't start without a contract. What's up? How are you even stuck in one spot? This isn't one of those skanky end-of-the-world deals, is it?" "Yes, I see what's happening out there. Meet the new guys," Finch pointed at each of them in turn, "Wil, Zé, Eli, and Ulf on the sofa taking a time out. Help me train Wil and extract everyone, alive. What do you want? Name your price. No, you can't be king of New York in the 1950's. Put it right out of your mind. Other than that, what do you want?" Mick nodded graciously at introductions, then appeared to count a big score he stood to get out of negotiations, before a sudden look of suspicion swept his eyes. Zé was impressed at his speed of expression change. "That's the highest starting offer you ever made," Mick noted. "What am I not going to like? I don't do suicide runs." "Wil takes point," Finch explained. "You back him up, train him on the fly, while fending off a metric shitload of trippers: rabid treasure hunters and high ranking Chthon agents, both. Seven days, high risk." "Uh," Mick looked agonized, closing his eyes (making Wil wonder if everyone around Finch had that mannerism). Mick sighed, then seemed to make up his mind. "Treasure? I know: you can't tell me till I'm there. Okay, in addition to usual perks: I get forty million in gold; I get to be ten years younger; I get two wishes; I see this one magnificent girl again in a bar in New York 1955; and I'm on your list of survivors, guaranteed." "That's pretty steep," Finch resisted. "One wish only, and no wishing for more wishes. Five years younger. And for New York in 1955, you have to talk to Wil: he's the one that would take you." Flywheel and Mick spoke at once; Flywheel groaned, "Oh no." "Who the hell are you?" Mick turned to Wil. Then Mick's eyes finally caught Wil's new gun, and froze. "Ah," he said. "That's interesting. Why does he need training if he already carries one of those?" Finch continued, "And you can't go unless you promise not to say 'hey baby, I'm from the future' again, to anyone—that's not funny. If you agree, get in here: I'm in a hurry. One more condition: go ahead and mess with Wil, but don't really hurt him, because he has permission to shoot you if forced." "Wil can outshoot Mick?" Flywheel sounded surprised. "Terrific," Mick sighed, then told Wil, "I'm at your service. Can I take ten or twenty more minutes to square away stuff?" "Sure, if Finch agrees," Wil nodded and glanced at Finch. "Wait, I have a question," Zé interrupted. "Did you really help Finch at Woodstock? You were in a Woodstock photo with her." Mick looked at Finch, who nodded. "Yes, we stopped the Woodstock disaster," Mick said. "Did she say why she does stuff like that?" "Not in so many words, no," Zé replied. "They might pay a good price for information," Finch warned Mick. "Sure you want to give it away free?" "First I have to prove I know something," Mick countered. "Here's my guess: I think she fishes for new culture when more people live. Ask Finch what new kind of music appeared after Woodstock came out well." "Heavy metal?" Eli guessed. Mick laughed and shook his head, then signed off. When Flywheel's head returned he said, "Sorry, Aleph is back on stage and won't call until she gets off in a few minutes." 17oct09
«
risky hunches
caching « A lot of my day job is about caching, in the sense you can't dedup(licate) content streams unless you can figure out whether you already have a copy of something, so a new one is extra. Of course, the hard part is predicting a node somewhere else already has a copy, so acting on this prediction is usually productive, and doesn't cost too much when wrong. I tell folks I expect memory hierarchy is never going away, so there's always going to be work in the space of picking what level of memory hierarchy does what, for a given app solution space. The individual bits of tech involved change, but the range of speed, latency, and capacity is likely to remain broad. The caching part itself is fairly cut and dried, though. The operational part is usually just "this part is cached" and all the interesting aspects are deciding how big that cache must be and how quickly its content changes, and how you plan to invalidate and handle recovery when something goes wrong. Caching itself isn't very interesting per se. I used to be really interested in storage. That was back in the day most folks were interested in GUIs, and no one needed one more guy arguing about that. Now everyone loves storage, so tech conversations are caught in another rathole as folks argue about bikeshed colors. Now I spend more of my energy on things other folks still hate to do in runtimes, often involving memory, timing, concurrency, and consistency. I live in a blurry boundary between architecture and implementation, since I do both, in a context where if you only did one you'd have a problem: a view that's too high, or too low. I get asked questions characteristic of each. Exactly how does this plan ensure it always works? And, exactly how is it actually done so the plan stays valid? I do both in a robust way, so my plans don't go cockeyed, and code following a plan has few bugs. Part of my style is a commitment to principle: first you decide what things should always be true, and which things you can't guarantee; then you avoid promising more than invariants guarantee. You need to be honest when a nice-to-have is just a pipe dream: tell them "no." normal « I've been feeling almost normal the last few days; "normal" is how I felt before (say) 2001, before my marriage really started to suck, after my ex stopped trying to act with any self restraint, burning through cash at a crazy rate. Normal included traces of optimism, workaholism, and liking my tech projects. Yesterday I noticed I was actually having fun, and that I was looking forward to what happens next in tech stuff I'm doing, instead of just grinding through, out of good form. Yes, I was trying to acquire this view by going through the motions, but I wasn't sure it would work. Now it seems like it did. If my spirits stay high, I'm likely to write less tech stuff here, since I was using this site as a crutch to replace natural energy and focus. Some of my tech writing experiments, lately, just aren't working well. I wrote quite a bit more on a code page, and the result wasn't clear at all, mainly because there was too damn much material. I suspect most careful things I do now are a bit too detailed to be clear when I write about them, given writing skills I have now. It's tempting to just write more language tools, and more async engine tools, and just not write about them, as long as I'm having fun coding. As a compromise, maybe I'll write only descriptions, without code; for example, material I wrote recently about lane-based fibers didn't show any code. I could write about a Lisp implementation the same way, focusing on high level generalities. Putting code in tech descriptions is too slow, because too much annotation is needed in explanation. Maybe a high level gloss without code will work. For some reason, fiction seems easier to write than good tech material, which surprises me. I almost wrote fiction all afternoon, but played with my kids more instead. Interestingly, the more fun I have writing code, the less point writing fiction seems to have. Maybe this is an old habit peeking through (from the past, when I tried to be productive often). So, um, if my tech writing seems to get less detailed, now you know what I'm trying to do: say less rather than say nothing. There's also a chance it will be fun. Writing about lane-based fibers that way was fun. (Yes, I know it was quite boring, unless you were thinking about coding fibers yourself.) I might also experiment with mostly writing fiction here, while mostly coding without significant docs at the same time, beyond high level glosses. 14oct09
«
vanishing points
annoying context switches « My lane-based fiber engine is starting to work. Yesterday I gave it a real run for the first time. Last week I just got the thread pool working (and made sure it shut down correctly, with all threads waiting nicely on a barrier). As a test case, I generated 128 bit CRCs of input files, with all file i/o done by async request to the thread pool, with all checksum code running in a lane fiber. Surprisingly, it ran correctly the first time. I expected a crash and a debugging session, but everything ran perfectly from start to finish. A fairly large number of things needed to work right, first time, for that to happen. Normally I don't think quite that carefully before trying code. But I feared getting a mess if I wasn't careful. I'm still trying to get meaningful stats. I wired up some time statistics, to see how much wall clock time elapsed, as measured by the hardware clock register. I compared the normal synchronous version with the async code. Since the latter could arrange for two or more files to wait at the same, I thought it might be faster. With two files, async was faster. But the more files I checksummed at the same time, interleaved by the scheduler, the slower the speed per file. Of course, fastest possible speed is not specifically the point; the main idea is to ensure async code doesn't block, and to have a lot of things going on at once. But I had a suspicion. What if pushing a request into the worker pool's queue caused the main async thread to block? That would happen if the pthreads library was tuned to aggressively context switch. I'd rather have worker threads act as second class citizens, getting time only when the async main thread blocked or had a long timeslice. But I suspected increasing elapsed time I saw for async code meant it was being usurped, adding latency from pre-emption by a worker thread. So I measured elapsed time across the call to push a request into the thread queue. Argh. Twenty percent of async code time went into pushing requests—locking a mutex and signalling a condition variable—when I expected this to be zero percent. That sucks. The amount of time to push a request exceeded time spent doing i/o in worker threads, which suggests maybe the async thread waited for the i/o after all. I'd really rather not write lockless request transfers to avoid provoking worker threads to pre-empt. Maybe I can use another queue of pending requests on the async side; then I can add N requests to the thread pool queue at one go, when the scheduler decides it's a good time, because all lane fibers are waiting—or because it's been long enough since the last time the pending queue was flushed. eyeballing game « I played the eyeballing game this evening, which lets you manually find the right point to satisfy various visual constraints. At first I did most poorly at bisecting a line; then later I was most likely to get a perfect zero on that one. My three scores this evening were (in this order): 2.34, 2.00, and 1.38, which set the high score in the last ten thousand games. Maybe I'll try it again tomorrow. I think I can get my score down to about 1.00 or so, in about two or three more sessions, because I had a long run of scores under 1.0, and only blew it with a couple scores of 4+ and 5+ that raised my average after a couple 2.2 near misses. My limit would probably be an average of 0.75 after a lot of practice. The main trick is to take your time and move your eyes around, until you stop seeing anything wrong. Finding a point equidistant from sides of a triangle takes a little thinking. (For that one, I let my eyes blur a little and try to see only the center point and a circle touching the sides.) Yes, I have a very good eye. In junior and high school, I was good at drawing and geometry. But art was never my avocation; it was just fun. I used to draw pictures for my sons to color when they were little. I could blow up any drawing freehand very easily. I can also draw freehand perspective reasonably well, from a point different from one I see. My ex's reaction to drawings I did for my sons was usually, "That's sick." She was always interested in art from a criticism and history perspective. But she couldn't draw. 11oct09
«
string-joined cans
lane queues « Let's resume where I stopped yesterday. (See the long lane fibers entry.) Object types not yet defined in detail typically involve one or more queues in lifecycle management. So I might seem to harp this aspect. (Please note I'm actually coding this model, so it's not just study. Of course I don't use these short names; I spell out Lane in full. This version is just easier to reason about, compared to using C++ notation.) To hide template notation, instead of writing Q<T>—for queue of type T—we might instead write TQ to mean "T queue." This shorter notation looks better when we add another letter to denote queue purpose. For example, rTQ for "ready T queue" is easier to read than rQ<T> which looks like symbol salad. So the ready lane queue and wait lane queue in each zone Z are named rLQ and wLQ. An identity, I (also written ID, pronounced eye dee), is a pair of integers (Ii, Ig) where Ii is an index (into an array), and where Ig is a generation (typically pseudo randomly generated). Zero is never a valid generation number, so an ID is invalid when Ig is zero. An ID is also invalid when Ig does not match the current generation number of the entity named by the ID; this is in fact the purpose of generation numbers. Each I identity is 32 bits in size when Ii and Ig are 16 bits apiece; otherwise an ID is 64 bits in size when each sub-part is 32 bits. You should think of an ID as just an integer; a compiler will pass an ID by value just as efficiently as a 32 bit or 64 bit integer. A lane, L, is a 5-tuple (E, I, A, eMQ, iMQ) if you add no other stats or metainfo, like time consumed by a lane. Each lane inherits elem E as a base, so each lane can be an elem in one (and only one) LQ lane queue; so far I needn't put lanes in more than one queue. I is a lane's identity; I is only valid when a lane is alive (spawned and not yet exited). A zone Z can lookup a lane by its ID, and this happens (for example) when a msg M (defined below) is delivered to a lane by ID. A lane's arc A is the frozen state of execution for a fiber in this lane, as defined earlier, consisting of pair (K, R) to hold continuation and optional return value. Holding arc A is a lane's main purpose; the rest is bookkeeping. When a lane exits, each msg in the eMQ exit msg queue is delivered. When a msg is delivered to a lane, it arrives in the iMQ inbox msg queue. (Msg delivery might invoke callback code only, if no lane is addressed by lane ID in a msg.) Both eMQ and iMQ are doubly-linked circular lists of msg M instances in Q<M> format (using the intrinsic E links inside each msg). Listeners place a self-addressed msg M in exit queue eMQ to be delivered when a lane exits. When a lane waits in arrival of async msg M delivery, these arrive in inbox iMQ. There's a lot to say about contracts in msg delivery, with parts appearing above, below (in a definition of msg M), and later, when zone Z is described further. Why is the topic of msg delivery so complex compared to other objects? Because msg M behavior crosses a lot of dynamic execution domains. While this is also true of arc A when a jump J maps an in-arc Ai to an out-arc Ao, in that case it's outside the scope of lane engine control. Arc changes are what app code does when lane fibers execute. In contrast, msg changes are partly how the lane engine runs, with api crossing into scope of app code running in a jump J. For example, when does a msg M in a lane's inbox iMQ actually get delivered to a lane fiber? Only when pop() is called on zone Z inside a J function. Presumably this happens when J is called as the continuation of a wait() when a fiber runs again after blocking; the arrival of a msg when a lane is waiting makes a lane ready, by moving a lane L from the wLQ wait queue to the rLQ ready queue in zone Z. (Don't worry, I'll say this again later.) In that context pop() should succeed in popping a frontmost msg from the current running lane's inbox iMQ, or otherwise the lane should not have unblocked. A callback, Cb (as opposed to cond C), is a pair (Mf, p) where Mf is a function pointer of type void(*Mf)(M*) taking a msg M argument (defined below), and where p is an untyped pointer void* to allow extra state to be passed. This sort of callback is embedded inside msg M to attach executable code to a msg processed in one of two different basic contexts. When a msg M is sent to a thread pool's request queue (a threadsafe MQ) then Cb is called in a worker thread, and presumably p (and/or a node N inside msg M's handle H) indicates state needed by that blocking call. Otherwise, when a msg is delivered from a lane's eMQ exit msg queue by the lane engine's thread (not a worker thread), Cb calls Mf at delivery time if Mf is a nonzero function pointer. In general, callback Cb represents an optional something to be done when a msg M reaches either 1) a worker thread, or 2) a lane's inbox when delivered from another lane's exit msg queue. A msg, M, is an 8-tuple (E, Ik, fid, tid, K, R, Cb, H) if you add no other stats, metainfo, or extra callback methods. (In practice I use another callback besides Cb shown here, but its gnarly explanation suggests it is accidental and not essential complexity. Basically, sometimes you want callback layering, so one callback can call another one.) Each msg M inherits elem E as a base, so each msg can be an elem in one (and only one) MQ msg queue; so far, only one E is enough per msg. Integer Ik denotes the kind of msg, in case more than one type of msg is expected by a recipient. Both fid and tid are instances of I lane ID, denoting from lane and to lane—the sender and receiver of this msg. If tid is valid (with a nonzero Ig generation number) then tid identifies a lane to receive this msg when delivered. You can leave tid invalid if no lane wants to receive this msg; perhaps everything is done by Cb when called in a worker thread, or when some lane exits. Lane fid is either a lane that exited, or a caller of a non-blocking method in a worker thread. Handle H refs an optional node N if this msg needs extra state to work; H might typically be a ref to frame F that sent the msg, since it can hold any state needed. The kont K plus return value R in each msg M together look a bit like an arc A, except they are not used together as an arc instance. Instead they're used at different times; however, one of the uses is turning them into an arc A in case a msg needs to be processed by a new or old lane fiber. When a lane exits, every msg M in that lane's eMQ exit queue is delivered, but first the lane's last return value is copied from a lane's A to this R inside M; so a lane L notifies all listeners with a copy of the lane's last known return value. The kont K in a msg is a good way to encode how a receiving lane fiber should respond to a msg when it arrives in a lane's iMQ inbox. When a lane waits on the inbox and finally gets msg, the K inside might be exactly the continuation the fiber wants to call next, perhaps with the msg tucked into a frame for posterity. Basically, it's hard to effect a "join" operation between a waiting lane and an async operation without using a K continuation. How should you handle waiting for several related async operations? Say you issue N async requests, where each will reply with a msg arriving in a lane's inbox. After starting every request, you wait for a msg to arrive, then pop it from the zone Z when the wait returns. Until all N msg replies arrive, save every msg in the frame of kont K in the lane's arc A. Since every msg popped this way is inside no queue, you can put an MQ msg queue in your frame and store them this way. Once your N-way join is complete, pop all each msg from your frame's MQ and free them (by returning them to the zone Z). More complex forms of async coordination can be done using mutexes and condition variables, which are supported by mutex X and cond C, defined below. These are basically clones of pthread mutexes and condition variables, but have nothing to do with native threads. Instead, calling any mutex X or cond C method amounts to talking with the lane engine scheduler, with a net effect of scheduling your lane to run mutually exclusively with other lane fibers using the same mutex, by blocking a lane attempting to lock a mutex that is already locked. Similarly, condition variables are also cooperative and effected by the lane engine scheduler. (See literature on monitors for background.) A mutex, X, is a 6-tuple (E, Z, S, LQ, lid, Ic) providing cooperative mutual exclusion for lane fibers using one mutex. Fibers are scheduled cooperatively, not pre-emptively, so one fiber cannot interrupt another inside a single jump J. But a fiber context switch can occur between any two jump calls made by the trampoline. When a lane fiber needs exclusive access to some resource across spans of multiple continuation calls, a mutex X is needed to block other lane fibers should they try to use that same resource during the same critical section. While one fiber has a mutex locked, that lane's ID is stored in lid so an explicit representation of a wait-for graph can be analyzed at any time (to see deadlocks, for example). When a mutex X is already locked, any other lane attempting to lock the same mutex will block, moving that lane from zone Z's ready queue rLQ to the LQ lane queue inside X. When a mutex is later unlocked, at least one lane can be popped from LQ and returned to the ready queue. (Perhaps it's best to simply lock the mutex immediately on behalf of the popped lane that becomes unblocked.) Other state inside a mutex X is just bookkeeping. Each mutex inherits E as a base so each mutex can be put into a free list or a used list in zone Z. The used mutex queue uXQ in zone Z is where all currently used mutexes live, so we can later inspect all synchronization primitives when analyzing a wait-for graph. C string name S is a mutex annotation showing a human readable name for a mutex. A pointer to the zone Z containing the lane runtime is also put inside each mutex X to ensure access (and to compare to Z stored in each cond C that uses a mutex). Integer Ic is a count of cond C condition variables currently using the same mutex, helping detect errors like freeing a mutex when still used by a cond. A cond, C, is a 5-tuple (E, Z, S, LQ, M) providing cooperative control to block lanes waiting for some condition to occur. Each cond must refer to a specific mutex M that must be locked when a cond C is used. (This is just like pthread style api.) Other than M, all state in cond C is very similar to parts of mutex M with the same names: so S is a C string name annotation; E is a baseclass so zone Z can put conds in either a free or used queue; Z is known by pointer to ease access; and LQ is a lane queue containing lanes blocked on this condition variable. Let's revisit zone Z now and finally define some of the internal state of a lane engine runtime. (I'm not going to define how the thread pool works, but it's very similar to what is shown on the thread demo). To avoid saying Z is an N-tuple for a large value of N, maybe we should group parts of Z in sub-objects. Hmmm, maybe later. A zone, Z, is a big object containing all the runtime state of a single lane engine. Each Z is a 14-tuple (L, A, V, P, tMQ, dMQ, fMQ, rLQ, wLQ, fLQ, uXQ, fXQ, uCQ, fCQ) with ten queues consisting of three msg queues: thread, done, and free; three lane queues: ready, wait, and free; two mutex queues: used and free; and two cond queues: used and free. The default vat is V, and the default pool is P, both used for allocation. Parts of zone Z not shown consist mainly of lots of statistics and config state (to specify fixed population sizes and timeslice parameters, etc). When no lane fiber is currently running in the trampoline, both L and A are nil. But inside the trampoline, when a J method is still running, L is the current lane and A is the current arc in that lane. A zone runs only one lane at a time: L is that lane, and A is either the arc inside L or a second alternate on the stack inside the trampoline. (When analyzing a global snapshot of state inside a zone Z, every lane can be examined to see an arc inside that lane, with the sole exception of lane L, which might have its arc in a zone's A instead.) The thread msg queue tMQ is the outbound queue of blocking call requests handled by a worker thread pool. When a request is done, the msg returns as a reply in the done msg queue dMQ. Finally, the free msg queue fMQ is a pool of currently deallocated msg instances. (Currently, blocks of consecutive msg instances in an array are allocated in 8K slabs. The same is done for many other object types, like mutexes and conds.) The ready lane queue is the set of lane fibers that can currently be scheduled by the lane engine. None of these fibers are blocked. The wait lane queue is the set of lane fibers blocked until a msg addressed to that lane is delivered, either by reply from the thread pool or by notification from another lane's exit message queue (see eMQ inside lane L). When any msg is put into a lane's iMQ inbox, if that lane is in the zone's wLQ wait queue, it gets moves to the rLQ ready queue, because msg arrival wakes a lane when waiting. In zone Z, the lane engine's trampoline polls the dMQ done msg queue for thread-safe replies from the thread pool, perhaps as often as between each timeslice. A msg with an invalid to lane is simply deallocated; but any msg addressed to a lane with a valid ID is appended to that lane's inbox, causing the lane to wake if it is waiting. Note message delivery has no effect on either mutexes or conds, which alter lane scheduling themselves when a queue in either X or C changes. (Todo: describe the trampoline.) 10oct09
«
tying knots
lane fibers « (I thought I was going to finish this when I started; but after two hours, I still have several things more to define, including msg M, lane L, cond C, mutex X, and the guts of zone Z, and those are the complex objects in this model. I guess I should do them tomorrow, instead of staying up into the middle of the night.) I recently wrote about a hand-rolling fibers in C++, which I dubbed lane-based fibers, aiming to define a fiber as a continuation in a channel called a lane (like a highway lane or swimming pool lane). In this model, a lane contains a frozen image of what a fiber is doing, consisting of little besides the continuation, an optional return value (if the continuation is an upward return), plus any bookkeeping for a lane engine (aka fiber VM, aka lane VM). For extreme brevity, say lanes to mean lanes-as-fibers. This piece today is about my efforts to get lanes to work. It's been going slowly. My code seesaws between elegance and complexity. When it gets complex, I think I'm doing it wrong. (Actually, I think something more like: what the hell am I doing?) Then I go through a round of simplification and say, oh, this is still cool enough. But now I grasp why folks don't take this sort of approach more often. In C++, lanes seem at least one order of magnitude more complex than the basic idea seems to require. C++ adds all this accidental detail obscuring the essential issues. I find that I must keep a lane model in my mind's eye because code hides too much of it. This seems wrong to me. At one point I suddenly realized lanes would be more clear in macro assembler than it is in C++. What's wrong with this picture? Apparently I may have to do this one or two more times before I like the end result. I'm having a "plan to throw one away" experience here. So far it looks like the first rev will be good enough to use, so it's not a waste of time. But I might be the only person who easily understands what the code does. That's not a good sign in code. Good code should clearly do what it purports to do, in a way readable to any fairly good programmer. What I have now is twisty. At one point I was thinking I might write another Lisp (called Lathe) directly using this C++ level async api with hand-rolled continuations. But so far it seems way more complex looking than ordinary synchronous code. That is, it looks more complex than what it's doing, which means the way I expressed it must be wrong. How should I have expressed it then? I'm thinking I should express what happens in a high level syntax—Lisp syntax, for example—and then generate the goofy parts, compiling high level syntax to C++ or C, which would simplify the basic expression, and remove some of the element of human error from translation to C++. But that begs a question of what first synchronous Lisp processor is used to bootstrap an async version. So now I think I ought to write a simple sync version of Lathe so I can use it to compile parts of the async version. But that basically amounts to a longish hobby project at home. I try not to get myself too easily enthused in longish hobby code projects now. But in this case, it seems like it's more likely to be worthwhile than things I aimed to do before. Why do I think that? Because what I'm seeing in the clunky version of lane-based fibers now looks promising despite the ugliness. It looks like it will be able to do something—async simulations of distributed systems—now currently very hard to do with tools at my disposal. And it has enough value. The rest of this is a kind of pseudo-code description of lane-based fibers. I want to write another view of it here that's simpler, so I can hold the image more clearly in my mind. Then if I get stuck in C++ again, I can doodle in notation like this to resolve an issue more quickly. A node, N, is a refcounted baseclass for all other refcounted objects (strongly resembling a yn node class in the node demo) supporting weak and strong refs, permitting a node to release resources when no longer used, before it's deallocated when no longer referenced. When a node N is created, you must supply a handle to hold the first ref, and a vat to free that node when it hits zero refs. Nodes can print themselves if you care to write the code. A handle, H, is a smart pointer to a node subclass. In template notation H<T> means a handle to type T, where T is a subtype of node N. To hide template notation, for each type T we might define typedef TH which means the same thing as H<T> (that is: a T handle.) In general, suffix H on a typename means a handle specific to the name's prefix. A generic node handle has type NH (aka H<N>). A main purpose of H is to ensure nodes are refcounted correctly, despite assignment, copy construction, and destruction along any C++ code path, including exception throwing and early exit via return in the middle of a method. (A secondary purpose of handles is to enable auditing to find refcount leaks on a backtrace by backtrace basis, if you write the audit code. The refcount of a node is defined to be a count of refs from handles only. You must use a handle to refcount, and nothing else can be used to refcount. Refcounting then becomes spatially oriented, based on where nodes are counted, rather than temporally oriented based on when counts change. Blame is only easy to audit in a spatial sense, with backtraces used as measure of spatial location. In node usage, memory safety is simply a matter of being able to see—in a local scope—at least one handle holding a node in a given context.) A vat, V, is a memory allocator specific to nodes, so when a node hits zero refs it can be returned to its source vat. Presumably this makes it easier to pool nodes and statically partition memory in fixed sized pools for nodes, if you're willing to tune a vat to fit a very precise app profile. (Otherwise a vat baseclass will simply use malloc() and free(), if you can't be bothered.) A pool, P, is another kind of memory allocator specific to ordinary memory blocks that aren't nodes. (A base pool class also uses malloc() and free(), so you need to subclass if you want something more complex to occur.) A jump, J, is a pointer to a function called by a lane fiber trampoline. From the perspective of a lane fiber, all functions have one and only one type signature, of type J. A jump method is a leaf call from a trampoline's view; inside a jump leaf, any synchronous C or C++ code can be called, but the lane engine doesn't know about this. Each jump called by engine's trampoline is inside the current lane's continuation, denoting the next thing a fiber does when the scheduler lets it run. The type of J is int(*J)(B*), taking a pointer to a nub and returning an integer of type yaw telling the trampoline where to veer next. A yaw integer, Y, has three possible values (negative, zero, positive) which denote exit, yield, and go resembling colors of a stoplight: red, yellow, and green. A negative yaw integer returned from any jump method J tells the trampoline to make the lane exit and return the last value to any listeners. A zero yaw tells the trampoline to yield the remainder of the current lane's timeslice. Postive yaw means a lane wishes to go further, timeslice permitting. A nub, B, is a triple (Z, Ai, Ao) denoting what any jump J needs to see. A zone Z (defined below) is all the runtime context of a lane fiber engine, providing access to anything a fiber needs to reach. An arc A (defined below) is the current fiber state of a lane, including code, stack, and optional return value. The input arc is Ai; the output arc is Ao. In effect, every jump J acts like a function of two arguments (Z, Ai) returning two values (Y, Ao) so Ai denotes what a lane should do now, and Ao denotes what a lane should do next when the scheduler calls the J inside Ao (if the return yaw integer is not negative for exit). (A nub B is a transient object defined on the stack in a trampoline method in the scheduler that actually calls each J method. The physical space for arcs Ai and Ao swap on successive calls, so Ao becomes the next Ai passed to the J extracted from Ai. The only reason Ai and Ao are two different arcs, instead of one inout parameter, is to minimize risk in updates under refcounting. If any J method treats Ai as mostly immutable, except for the stack inside, things should be good. Two arcs alternate to avoid extra refcount work.) To refer to an idea of continuations, I use several different short words with similar meanings to emphasize roles with minor differences. Since a continuation is viewed as an arrow denoting where a lane should go (like a vector in a point space), some of these words are synonyms for arrow. The exceptions are kont and knot, which both refer to continuation; a kont continuation uses a handle to refcount its frame, but knot does not, so knot is cheap, transient, binding of code and data without frame refcounting. The term way is a specialization of jump with direction, and term arc is a specialization of kont with payload added. A way, W, is a pair (J, S) where S is a C string representation of J for human readable presentations (e.g. in backtraces) which might include a longer type signature than J's uninformative minimalism. (For example, you might want to describe J's stack format, especially the input parameters which cannot appear in uniform J types.) In general, W gathers all metainfo about J we want to know. For now, a human readable C string is helpful on platforms (like the Mac) where it's hard to get the symbol string for a code address. (Think of way W as meaning the same thing as code, denoting which way a fiber goes next in its lane—direction without state or payload. Each way is now just a J function to call in the trampoline, plus a function name annotation so we can always render return addresses in backtraces.) A kont, K, is a continuation consisting of code address and data stack, binding together executable code with an activation frame. Each kont is a pair (W, FH) where FH is a frame handle (of type H<F>) holding a reference to an activation stack frame, where a frame F (defined below) is a node N subclass, so stack frames can be refcounted to build spaghetti stacks in the heap. In practice, most kont K instances are embedded either in A arcs or F frames, to represent either a lane fiber's arc frozen state of execution, or the return address and stack of a caller in a frame. A handle to a frame keeps that frame alive. A knot, Kn, is a proto-continuation pair (W, F) which is just like a kont pair, but without refcounting a frame. In practice, a knot is a very cheap, transient continuation used in dispatching without refcounting too soon. If you like, think of kont K and knot Kn as being exactly the same thing, but letting you choose whether cost of refcounting makes sense in a given context. A frame, F, is a refcounted stack activation record deriving from node N for refcounting; this means when constructed, any frame needs a handle H to hold a first ref and a vat V to free the frame when the last ref goes away. Frame subclasses add any members needed to handle parameters, locals, and any other state needed while a jump J uses that frame. Looking at base class members only, a frame is a pair (W, K) where way W is the first entry point using this frame and kont K is the continuation of the caller used for replies. (Returning in continuation passing style, CPS, works by invoking a caller's continuation.) A backtrace is the list of activation records from the topmost frame to the base of the call tree; in text form, this is the sequence of names in the way W of each frame in the list of frames chained through the K continuation. (We need to save the way W of the original entry point when a frame is used in order to mimic normal backtraces in plain old C, because otherwise we won't be able to see what method we're "inside" when return addresses inside a method are completely different jump J methods.) A ret, R, is an optional return value that might be used when returning from a callee to a caller, since a continuation kont K does not include a return value payload. Because a return value might refer to allocated space (for example, inside a callee frame that is returning), each ret R contains a node handle NH to hold a ref to any node that contains part of the value returned. Otherwise a ret R contains a small amount of integer based state, including a bare minimum of metainfo in the form of a type field to denote type of value. Each ret R is a 4-tuple (NH, Is, It, Iv) where Is is an integer status (typically understood to be an errno code), It is an integer type, and Iv is an integer value (if this is enough space—otherwise a node might be necessary). The easiest way to return a large value to a caller is simply to pass a callee frame ref in node handle NH. An arc, A, is the frozen state of a lane fiber continuation. Each arc is a pair (K, R) where kont K is the continuation for what a fiber should do next, and R is a return value (only used if K is a return address inside a method). Because the stack inside kont K is refcounted, and because a large value in return value R is refcounted, an arc A instance can be embedded anywhere. So the arc inside each lane instance is just how an arc is used to represent a fiber. An arc can also be put anywhere else; for example, you can put an arc inside an event in order to represent a lane fiber that should be created and run when an event fires. (In general, you can represent signal handling by saving signalled behavior as arcs that can be put into a newly spawned lane to execute at need.) An elem, E, is a queue element for doubly-linked lists similar to yqe in the list demo (cf «), used for intrinsic linking for all objects typically resident in one and only one queue in the lane fiber runtime. Several objects defined below are subtypes of E so they can be efficiently put into exactly one queue Q at a time (per inherited E base, if more than one occurs). Note definitions below might say type T has an E when in fact T is an E. There's not much point in distinguishing has-a and is-a in this presentation. A queue, Q, is a primitive, double-ended, circular linked list whose members are all elem E instances (or subtypes of E). Note when any elem E is inside a particular queue Q instance, that elem knows which queue it is inside. (The actual E code looks like yqqe which is not now in the list demo.) So queue Q can assert it actually contains an elem in any given operation. Additionally, when queue membership is used to represent object status—for example, a ready lane is inside a specific ready-queue—we can test for that status just by looking at the elem's queue. Note we can use template notation Q<T> to say a queue Q of elems that are all subtypes of T, where T is an E subclass. A zone, Z, is a kitchen sink for all state related to running a lane fiber engine virtual machine, including all lanes that can be allocated, and all other objects of limited population associated with a lane runtime. For example, each zone has a pool of n worker threads to handle blocking system calls. If you like, just think of a zone Z as all the global objects in a lane fiber runtime, but gathered in one place so you can have more than one zone in an address space. Each zone contains at least one vat and one pool, so anything allocated by a lane fiber can be allocated from the lane's zone. 09oct09
«
endless treadmills
extracurricular code « When I interview job candidates, the single most positive sign I ever see is an interest in code when no one else cares: outside school and outside work, when no one is demanding the result. I like to see a candidate care, because if they don't, typically they're not very good. Maybe I should put it the other way around: those who obsess about problem solving in any form are often head and shoulders above folks who merely respond to incentive. Why is that? Because they have more experience solving problems, since little stops you from doing it all the time. Difference in wall clock time spent solving problems can vary by two orders of magnitude when you compare habitual thinkers with sheep who follow orders. The result is something qualitatively different in ability, just from getting in the hours. Brilliance is nice too, but isn't a substitute for hours if you want stable, useful results. Yes, you see diminishing returns, so one hundred times as many hours does not mean a hundred times more skill. But it can mean ten or twenty times as much skill. And yes, having twenty times more skill doesn't mean twenty times as productive, but it can easily mean three or five times as productive. And more importantly, it can mean being able to solve different classes of problem. Only self-motivated people can do greenfield work without enormous waste. Working on something extracurricular turns you into the sort of person with the skills needed. You aren't just gaming the system to look like a good candidate. You in fact become a good candidate if you can focus on something a long time and navigate through a minefield of distractions, dead ends, and weak triumphs. You get the best results if you make decisions yourself, calling the shots. It's hard to learn as much without making mistakes. In fact, getting a grip on mistakes, both actual and potential, is a big skill to learn. Some good people do manage to channel all their fine efforts into school and work, because they are lucky enough to have school and/or work of a sort well suited to absorbing a person's inherent self motivation. But in this case it's just a coincidence learning by focused practice actually happens on the clock. I don't care when you do it, as long as you do. If school mates are lazy, comparing yourself to them isn't useful. For you to finish school actually very skilled, an average student at school must seem like an idiot to you, as far as you'd like to partner with them. It's not being over-qualified to train to that level. It seems like anyone who's any good always feels over-qualified for their jobs, at least at first. You need to be good enough to take on responsibility for mistakes in the past and the future, made by folks before and after you. That's what makes you excellent. 08oct09
«
voting rings
endings « The story I'm writing isn't designed to end, so it may frustrate folks who like a story to wrap itself up in a tidy bow, like a typical end in a Hollywood movie, where you find out "what's going on." Instead, it's designed to be a serial, with new parts added as they occur to me. I'm the person I want to please with my story, not you. If I expected income from writing, which I don't, I might care more what someone else likes. Instead, I care more about trying experiments, so I learn what works better in a story, in my opinion. The recent fable part isn't as good as parts I wrote just before, but that's okay for several reasons. First, it helps establish parts of the tale might be boring; other parts might seem better in contrast. I haven't talked about contrast much yet, but it does matter a lot. A lot of human perception is contrast-based, and resulting illusions work even when you know about them. In writing, one of the illusions is that "realism" is relative. The most real part of a story only seems so in contrast to less real parts. Fables act as nicely unreal parts for contrast, so they serve a functional role. Second, the fable helps detach my narrative flow from Finch's plans. This is important because Finch's plans will never become clear, despite your suspicions. So even though Wil and Zé learn a lot more, they never feel like they know what's up, and thus neither will you. And third, Wil likes the fable; his view needs beefing up, and it's hard to compete with Zé's alternate reality. If the fable is dorky, that must mean Wil is dorky, contrasting with his experience via fancy high tech provided by Finch. I'm also trying to slow the story down. I think it's having a speed spike because Finch is under time pressure. Once that ends, it'll slow down. But it won't actually end, because as I said, there's no end. If I let it go too fast now, a slowdown can seem like a crash in pacing. So I'm just smoothing it out. I also need more internal texture for self-similar back references, so touching an idea again has a bit of resonance. The pig fable makes it easier for others to make fun of Wil. For example, whenever Wil is thinking, I like the idea of other folks asking, "What would Briar Pig do?" Just to tease him. 07oct09
«
dog eat dog
fable flashback « Zé had a chat with Wil about his Briar Pig fable after the first couple parts, before Wil closed the deal with Sly and Ira in the briar patch, to get a handle on where Wil was going, and to offer advice if Wil needed any. A couple weeks after everything changed (when Istar gave Zé the ring), Zé looked back on this conversation for clues, since it seemed Finch may have timed things in part by Wil starting his tale. Finch asked about Briar Pig in particular—perhaps to see if Wil was on track as she saw things. As near as Zé could tell—since Finch only replied to some questions in a very oblique manner—avoiding a paradox was hard if you did one of two things. First, you must avoid stopping a person from doing a thing you knew affected culture in your history. It could be done, but it was dicey. Second, you must avoid giving a person an idea before they'd have had it on their own accord, indirectly undercutting related ideas that might matter; new spin is also dicey. All this assumed Finch was what she implied, and not one of a half dozen plausible but weird alternatives. "I like strange parts of your pig fable, Wil," Zé began pleasantly. "What a surprise," Wil nodded slightly. "But you know what I don't like?" Zé raised his eyebrows, then continued before Wil could interrupt: "I wish I knew your characters already, instead of jumping in cold. I was hoping you'd work in appearances by—you know—us, or alternative versions of us: our little gang." Wil nodded vigorously. "Yeah, that did seem a problem. I was sorta planning to rewrite what I have, by inserting a dialog in the middle, maybe between me and Eli—he might make good foil. I can lace argument about a tale inside that tale, at risk of weakening cohesion. Does that sound awful?" "What am I? Chopped liver?" Zé objected. "Why can't I appear in a dialog? I make a good foil, don't I?" "Not really," Wil disagreed. "It's more like I'm your foil, since you ask leading questions so much. But I'll think about it. Any other remarks?" Zé cleared his throat. "What's the moral of your fable?" he asked. "Don't set millions of pounds of ammonium nitrate on fire?" Wil laughed. "Why does a fable have to be about just one thing? Maybe I have a bunch of morals in mind. How about this one: don't underestimate shoeshine boys." "Am I supposed to recognize this kid, Wally?" Zé asked. "Well, he's just an archetypical underdog character," Wil cleared his throat and pulled at his collar. "You're supposed to identify with him." "That's another thing!" Zé pounced. "Where the hell is Briar Pig? He doesn't even appear yet, except in side references. Wasn't he going to be the hero? The wolf gets all the air time. I like Sly more than Ira now. You're doing it wrong. And where did you get that name: Ira?" "Pigs are hard to name," Wil pleaded with hands spread. "Maybe it's short for irate, as in: I'm mad as hell and I'm not going to take it anymore. I picked it out of the air. Yeah, Ira's the hero. But there's actually a point to spending so much time on Sly's point of view, because this is how Ira sees things: Sly is an actual person, so being forced to kill Sly is a tragedy. Whereas from Sly's point of view, pigs are just food, so Ira isn't a person. And that's the problem. I plan to have Ira explain this to Sly, before he kills Sly, that is." "Don't tell me how the story ends!" Zé covered his ears. Wil wagged a finger at Zé. "You knew Sly was going to die from the beginning. What kind of story about little pigs doesn't kill the wolf in the end? But usually pigs are afraid, and in this story it's the wolf who's afraid by the end." "And telling how much Sly likes Tex Ritter is a way of humanizing him?" Zé guessed. "So I care, since he has an internal mental life?" "In part," Wil nodded. "Also, I hadn't quite given up on a joke yet, but I was unable to fit some material into dialog with Wally, without Wally taking over the story. If I had managed to get Wally and Sly talking about Tex Ritter—say because Sly wants to know when Tex's next movie comes out—then Wally would have told Sly about Ritter's son, John Ritter, acting in sitcoms in the future." "John Ritter was really Tex Ritter's son?" Zé wondered. "Yep," Wil nodded. "I liked the idea of explaining sitcoms to Sly, as well as movies like Sling Blade in which John Ritter played. But, you know, you can't put every funny idea you have in a story. There isn't room." "Tell me about it," Zé commiserated. "Well, are you going to finish the fable? What about Sly's brother Teo?" "Of course I'm going to finish," Wil insisted. "I'm saving Teo for a sequel, obviously. He's the main boss Ira has to defeat. The rest of Sly's chapter isn't very long, though, unless I ad lib a surprising amount." "Why is that?" Zé probed. "Do you ad lib a lot?" "I can't make the briar patch scene long without terrifying Sly," Wil explained. "And that's creepy. I ad libbed almost all the story so far. My rough outline was something like telegram, barbershop, shoeshine boy, oracular foresight, withheld warning, exit stage left, then the briar patch." "I have a request," Zé suggested impishly. "Have someone slightly colorful give Sly directions at the briar patch." "How about an old, crippled, blind possum Sly considers eating as a snack?" Wil offered. "Then he turns out not to be blind and crippled after all." "In cahoots with Ira?" Zé grimaced. 05oct09
«
vile gangsters
More fiction—see my last post for the first installment. albino poodles « Over the next ten minutes Wally muttered facts about the impending disaster, getting more anxious. Sly was impressed by windows breaking forty miles away in Houston. Moe wondered if they should do something. Maybe someone could stop the blast? Wally looked far away and shook his head. “Someone is trying, but it’s too late,” Wally claimed. “They really should have gone back earlier. Maybe they’ll try again.” Wally screwed up his face in puzzled concentration, chewed his lip, then looked at Sly. “Who are those guys?” Sly took a step back and shot a look at Moe hinting: the kid’s losing it. “Look, I gotta go,” Sly begged off, suddenly mindful of business, pulling cash to pay and tip handsomely. “I’ll catch the rest on the radio, but I’ll be back after a big meeting.” Sly winked at Moe. “In the briar patch?” Wally scratched at a flea. “But I just shined your shoes.” “Who said anything about the briar patch, kid?” Sly squinted suspiciously. Wally looked out front like something happened, but Sly saw no one there. Wally turned to Sly and asked, “Do you know two tall albino poodles? They’re waiting by your roadster with guns.” “Oh crap!” Sly moaned. “It must be Raph and Vern!” “You should really pay back that money you borrowed,” Moe suggested casually, sweeping up hair trimmings. “I hear they filed their teeth down to points.” “Yeah, yeah,” Sly dismissed. Then to Wally: “Thanks, I owe you one, kid.” Cutting over two blocks, Sly crept down a side alley until he could peek around a corner, and there they were: a couple vicious, lanky goons taller than Sly, with broad-brimmed fedoras pulled firmly over puffy white hair. Normally Sly didn’t give dogs time of day, but these two pink-eyed lunatics were crazy. After Sly left the barber shop, Moe gave Wally a nudge to get his attention: “Why did you warn him, anyway? It was an opportunity. Now we have to deal with him later.” With two minutes to go, Wally had paws over both ears and his eyes closed tightly, apparently hoping to shut out the distant blast only he could hear from here. Wally opened one eye to answer. “The pig knows Sly is coming,” Wally said simply, his wet nose shining darkly. Soon Sly’s roadster screamed down the road with just one new bullet hole in the passenger side door. “That was a lot easier than I expected,” Sly growled to himself in satisfaction. The two poodles were so aggressive they were totally predictable, and gave chase almost exactly like Sly expected. Then he circled—fast!— in a flank maneuver to his roadster. Easy, except for the gunfire. Sly had an uneasy feeling—like he was missing something related to his trip to the briar patch, but couldn’t quite pin it down. Could Ira flank him? Nah, that wasn’t it. What the hell: the morning sun was glorious. Sly enjoyed the drive immensely. In his mind’s eye, Sly played Tex Ritter, the singing cowboy, riding a fine horse through a beautiful Arizona morning, composing a song to perform later. Sly couldn’t sing, except in his imagination, where he had a voice like an angel. For some reason, Sly always played a good guy in his daydreams. Tex Ritter was his hero, except for Tex’s duds—so Sly always pictured Tex in a zoot suit. Sly usually pretended his automatic pistols were cowboy sixguns when he drew them. 03oct09
«
year of the pig
Here's some fiction I'll integrate with other parts once I write a bit more to cover the episode through the actual briar patch scene. briar patch « Wil started drafting a fable about Briar Pig just before Falk's first visit (which Wil no longer recalled) after Zé begged him to put something in writing. How could Wil refuse? So Wil began his tale when the wolf gets a telegram, because starting at the beginning was so old hat. Let readers figure it out, Wil decided, skipping ahead to enter when the fun begins. Zé wanted some anachronistic time detail if possible, and Wil was glad to oblige. So this first draft had more weird parts than Wil might normally add. The seventh little pig—the last survivor of seven brothers—built his house in a briar patch. All houses built by his brothers, weak or strong, had fallen eventually. So Ira no longer put his faith in a single strategy, and fully expected to be found by the two remaining wolves, Sly and Teo. The first wolf, Al, was killed by simple trickery and a pot of boiling hot water; Al’s brothers Sly and Teo declared a feud, hunting the pigs. Only Ira remained: all his brothers had been killed. In hiding, Ira planned and prepared to be discovered, training and guarding against stealthy attack. The wolf reached Moe’s barbershop at 8:30 sharp with a telegram in his pocket and a big smile on his face, flipping his lucky coin: a slightly bent liberty half dollar that saved his life in the war. It was a beautiful April morning, but any morning would be fine, given his mood. An old fashioned calendar in Moe’s shop read Wednesday, April 16, 1947. Sly’s lucky day: barbecued pork day. Sly stopped to admire his zoot suit in the plate glass outside before entering. “Damn, I look good,” Sly told himself, then paused to whistle at a nice pair of passing gams. Ordinarily Sly would give chase to ask her out, but he had a date in a briar patch he hated to break. In Sly’s pocket was a telegram he received today by general delivery at Western Union, which read: PIG FOUND HOLED UP IN BRIAR PATCH
DUG IN DEEP BUT YOU KNOW WHAT TO DO
WATCH FOR TRICKS WITH THIS ONE TEO
Sly did indeed know what to do; his reflection’s fangs gleamed in early sunlight. Today he packed a matching pair of Browning M1911 automatic pistols with full clips of .45 ACP ammo. Sly was a superb marksman, but it didn’t matter today for close and personal work. He was briefly tempted to fetch a tommy gun—he didn’t pack one in his BMW 328 roadster—but Sly wanted to look in Ira’s eye when he pulled the trigger. Following a haircut and shoeshine, Sly was off to the briar patch to whack the pig—right after some recreational bone breaking Ira had coming. “The usual?” Moe asked Sly as he entered. Moe was a heavyset bulldog with a rheumy eye and scary five o’clock shadow, but was smarter than he looked at first. Often Moe’s racing tips were good, but he refused to reveal his sources, and Sly liked that. Sly snapped his fingers and pointed. “That’s right,” he sang in a cool hep cat tone. “Gonna settle a score with a pig who got my kid brother before the war. Where’s that idiot nephew of yours? I need a shoeshine, too. Pa said never do business with scuffed shoes.” “Wally?” Moe shook his head with a lopsided grimace. “He’s not an idiot. He just likes shining shoes. I promised my sister I’d look after him. This pig—someone I know?” Sly shook his head with a smile. “Very unlikely. Forget about it.” Moe cut Sly’s hair and talked about baseball. Moe pointed at Sly’s shoes when Wally arrived, and Wally set to work with a grin. Sly decided it was the floppy black ears and round spectacles that made Wally look so dumb. Dogs are simply outclassed by wolves. “Who do you like for the pennant this year, kid?” Sly asked Wally with a smile. “The Dodgers,” Wally said with certainty, working Sly’s shoes. “Jackie Robinson’s going to be Rookie of the Year. But I don’t have the final score yet.” Moe coughed loudly and tried to change the subject. “Did you read about those tornadoes last week? Over 150 dead.” Moe’s voice betrayed him with a quaver. “Actually 181, just like I said,” Wally corrected with a whine. Sly’s smile slowly fell as he looked back and forth between Wally and Moe. “Are you serious?” Sly asked Moe. “The kid actually calls the games?” Sly could see they believed it was true. In Sly’s line of work, you got good at reading eyes, or you were dead. Moe thought about all the money Sly lost to Moe in friendly sports bets over the years, hoping Sly didn’t think of it too. Moe’s face vacillated between a half smile and a fearful cringe, wanting to put on a friendly face, but worrying it might seem insolent. “That’s very interesting,” Sly decided, pulling his chin, smile slowly returning, to Moe’s great relief. “Yes, indeed. I can make a lot of money with this, yeah... Hey, relax, I’m not gonna cut off your tail, or anything, Moe. But don’t tell anyone else. Okay? Right kid?” Moe mimed locking his lips with a key; Wally nodded, peeking at Sly out of the corner of one eye. “Sorry,” Wally apologized to Moe. “It just kinda slipped out.” “What else can you see, kid?” Sly asked Wally with a casual tone. “Did you see those tornadoes before they happened? How about fires?” “Sometimes,” Wally cast a pleading glance at Moe that said: get him off me. “Forget it,” Sly assured with a big, hearty smile: Wally was his new buddy. “I won’t bug you. Just keep me in mind. Hey, can you tell my fortune?” “Not right now, I can’t,” Wally shook his head, perplexed. “The explosion wipes out everything else that happens afterward, for now.” “Explosion?” Sly prompted with a worried look. “What explosion?” Wally looked at the clock, which read ten minutes to nine. “In twenty minutes,” Wally explained, “seventeen million pounds of ammonium nitrate on the SS Grancamp explodes, in Texas City, and hundreds will be killed—and thousands injured. There’s a small fire aboard now. The force of the blast is like a small atomic bomb.” Wally’s eyes rolled up. Sly’s mouth hung open, his eyes staring open wide. Then he shook himself and looked at Moe who shrugged to say this is all news to me. Trying to sound calm, Sly asked, “Where is Texas City? Nowhere near here, I hope?” “In Texas, of course,” Wally said with a snide look, like Sly did poorly in geography. “Between Houston and Galveston on the coast, I think. Many whole city blocks are simply obliterated. I’m trying not to think about it.” Wally’s eyes started to tear. “It’s horrible.” “Does obliterated mean destroyed?” Sly wondered, wishing he read more. But then he’d be a loser like this kid, who didn’t see all the money in this. “Damn, there’s no way to make money off this now. If I say something to anyone, they’ll think I was involved.” Wally gazed at Sly with an expression Sly couldn’t read because it implied an idea Sly refused to grasp about himself, even from another’s view. Sly guessed it was related to the you’re a monster look he sometimes got when he said too much. Whatever. After Sly showed the kid how to rake in the dough, he’d finally see the light. Until then, maybe Sly should beef up his image. After all, the kid might be able to give him useful warnings. 02oct09
«
damn yankees
error handling « I don't like exceptions much, mainly because they're too easy to silence after the fact, but also because they make inference from evidence difficult. Note I'm not actually going to say much about exceptions here. Instead I'll focus on what I do instead, in day jobs, when we have no exceptions—like now, for example. I don't dislike exceptions enough to ignore them in a new language design; I guess I'd implement them, but without giving folks an impression they increase safety. (I think coders do a better job when constantly nervous.) What do I do instead of exceptions? Assertions, log messages, and error codes. Asserts are a nice way to bring code to a screaming halt, just like a segmentation fault. I lace code with asserts to stop violently when something forbidden or unexpected happens anyway. They aren't just for debugging; they stay in production code. Sometimes you'd rather a server restart than keep running after something really corrupt is seen. When QA folks test a system, death by assertion is harder to ignore, making it difficult to pretend everything is great. Obviously you want asserts never to fire, just like you don't want segmentation faults from bad pointers. In my work environment, log messages also work well, because QA folks don't ever want to see log messages either. So when they appear, I get a nice report. I log a message, sans assert, when something puzzling is seen that isn't fatal, just disturbing—or when I think it might be possible to reach an edge state, and I want to see evidence if it ever happens. I often go this way when I know how to handle a problem, if it occurs, but I want to know if it ever happens. I also often count them, so stats can reveal this count if you look; this might help you diagnose some other problem, since you can ask "did X ever happen?" and get an answer. Incidentally, when a system I use does have exceptions, I like to log and count them, so I can answer this question: Did an exception ever occur during this test run? When the answer is yes, sometimes I also instrument exceptions so a hashmap of backtraces for every exception thrown is captured, so I can see backtraces to study what happened. Finally, I use error codes in api presenting a contractual interface to other developers. Then I try to detect incorrect api usage. (I'm pretty draconian about checking arguments, so this hurdle makes a good diagnostic for others.) Some error codes denote expected conditions—for example: you didn't give me enough data to do anything that time, sorry. These codes must be handled by callers if they care about success. Other codes describe severe problems, and probably can't be handled well, other than logging so a developer can follow breadcrumbs later. I usually log these myself too, so it won't be my fault if an error is ignored higher up in a call chain; this makes it hard for a client of my api to pretend everything is going great, just by closing their eyes. What's the best approach? Try to design code so errors can't happen when you use an api correctly, then viciously pursue incorrect api usage, asserting on any violation. Then natural selection makes this go away, just like crashes on bad pointers. When errors can still occur, make them normal in the sense an approved course of action is defined, supported, and tested to see if it really works. Ultimately, you need to propagate an error so it invalidates anyone who depended on correct results. In networking, for example, you might have to kill a connection which would transmit the wrong data as a result of an error; this is better than munging data. In state machines, I make some error types persistent: once such an error occurs, a machine never returns success afterward, even if a client ignores earlier errors. How do I do this? Easy: I just count the number of persistent errors; a nonzero count prevents future success. This helps avoid loss of errors when clients get confused. |