OHMS BLOG

Friday, November 23, 2012

OhmsBlog

Announcing dblohm7.ca

On October 15, 2012, I started working at Mozilla Corporation. As an open source project, Mozilla strongly encourages contributors to blog about their work. I decided that the best way to meet this objective is for me to create a new blog that specifically focuses on my software development activities. The new blog is available at dblohm7.ca and will be syndicated to Planet Mozilla.

What does that mean for the OhmsBlog? Not much, really. I will continue to use this as my personal blog for musings and for technical posts that aren't software development or Mozilla related. I will also continue to use it for supporting my Android apps.

Sunday, June 17, 2012

technology

How to switch a Cisco DPC3825 / EPC3825 to/from bridge mode at will

DISCLAIMER: This blog entry is unsupported. It outlines the steps that I successfully taken to switch this device into bridge mode. I am posting this as a courtesy to people who would like to know how to do the same. Having said that, my work here is done and my problem has been solved. This technique might not work for all firmwares. It requires knowledge of HTML and HTTP. If this isn't working for you, please do not post comments or send me emails asking me to solve your problem for you.

A few weeks ago I had to help somebody deal with an annoyance: They had a Cisco DPC3825 device that is a combination cable modem + wireless router device. They were unhappy with the device's router functionality and wanted to set it to "bridge mode," i.e. disable everything but the cable modem.

This is easier said than done; this device had its firmware customized such that no user interface was available to change such things. There were no form fields present pertaining to the device's mode. I found some limited information on other websites. Many of these users had devices with firmwares that were different from the one that I was working with. In their cases, the form fields for switching modes were present in the HTML but hidden via CSS.

On the forum website, their solution was to remove the display: none from the field's CSS, making the form field visible. I was disappointed with this solution, as it wouldn't work in my case. On the other hand, I couldn't help but notice that the forum users had posted a few images of their HTML. Indeed, one of the screenshots includes the necessary <select> tag, whose name was "working_mode"! We weren't finished yet, however: I still needed to figure out which value to use for the hidden field. I noticed that in other <select> inputs, the firmware set the field's value to the 0-based index of the option in the select. The screenshot showed that "Router Mode" was the first option, implying a value of "0", followed by "Bridged Only," implying a value of "1". This means that one can, as part of the administration form, just insert an additional field. I'm lazy and I only needed to execute the command once, so I just used Firebug to inject a hidden input into the administration form. Look for the

<form name="administration" method="POST" action="/goform/Administration">

tag and add the following:

<input name="working_mode" type="hidden" value="1">

I submitted the form and was then presented with a screen informing me that the device was rebooting. When it reboots in bridge mode, the device resets its IP address to 192.168.100.1. To switch back to router mode, do the same thing but set the input value to "0".



Of course you might want to script all of this and POST the form directly.

Wednesday, March 14, 2012

technology

How do I reference TrustedInstaller?

I was editing some ACLs on my Windows box the other day, and I wanted to assign ownership of a directory to TrustedInstaller. The trouble was that I couldn't figure out how to specify the TrustedInstaller user into the security editor. After scouring several blog posts I finally found the answer. Here it is for posterity:

NT SERVICE\TrustedInstaller

Wednesday, February 08, 2012

code

Announcing Audio Auto-adjust

As I've been chronicling in my Adventures in Android series, I've been working on an Android app. I'm pleased to announce that today I have published Audio Auto-adjust 1.0.0 to the Android Market. I'd like to take this opportunity to provide a brief overview of what Audio Auto-adjust is and how to use it.

While there are numerous volume control apps already on the Android Market, I wrote this app to because I had some very specific needs that I wanted to address. I wanted an app that didn't leave services constantly running in the background, yet I wanted an app that would be responsive to system actions. In particular, I wanted to be able to save my audio settings as presets, and then associate those presets with actions.

The Main Activity



Here is the main activity as it appears in the portrait orientation. As you can see, the various audio streams are easily adjusted by moving the slider controls.




The toolbar across the top provides three widgets. The first button on the left toggles silent mode. The next button saves the current volume configuration to a preset. The third widget is a dropdown that displays the currently active preset (if any) and allows you to switch to a different preset. This widget is only enabled when there are two or more presets that have been saved.

Notice that some of the streams are identified by hyperlinks. Those hyperlinks lead to context menus for additional configuration settings that are permitted for the selected audio stream.

To access Audio Auto-adjust's advanced features, select your phone's menu button to activate the main menu. Note that the Edit Presets and Actions options are only available if you have already saved at least one preset.



Edit Presets Activity



This activity lists all the presets that have been saved in Audio Auto-adjust. In this example I have saved two presets, "Foo" and "Max." Touching one of those presets will command Audio Auto-adjust to switch to that preset. Long-pressing a preset, however, raises a context menu that allows you to modify that preset. For example, long-pressing the "Foo" preset presents this menu:



Actions Activity

The most powerful feature of Audio Auto-adjust is the ability to associate presets with system actions. My personal use case for this was when I enter my car and my phone connects to my vehicle's hands-free system via Bluetooth. Upon receiving notification that my phone has connected to my car, Audio Auto-adjust will automatically switch to an associated preset.

NOTE: In order to configure Bluetooth events, you must enable Bluetooth on your phone before entering the "Configure Actions" activity.



Each row in this activity lists a system action that may be associated with a preset. Actions containing "(None)" are simply ignored by Audio Auto-adjust. To associate an action with a preset, select the action's corresponding dropdown, and then choose a preset from the list.

A Note About Precedence

The order of the listed system actions in the "Configure Actions" activity affects the precedence of those actions. Entries that are located higher on the list are given a higher precedence by Audio Auto-adjust. If more than one system event is activated at the same time, the action with the highest precedence is the one that will be activated by Audio Auto-adjust.

Precedence may be adjusted by placing your finger on the grip on the left side of the row, and then dragging it up or down to your desired position.

Settings Activity

I've tried to ensure that each preference in the settings activity contains a useful summary. Here's a brief overview that goes into a bit further detail:

  • Volume Control Stream: Chooses which audio stream will be adjusted by your phone's hardware volume controls. Note that this setting only applies when Audio Auto-adjust is running in the foreground.
  • Background Notification: When enabled, Audio Auto-adjust will post a notification whenever it switches presets while running in the background.
  • Play Sounds: When adjusting volume settings, play sounds to illustrate the loudness of the setting.
  • Tie Ring Volume: When set, hide the controls for the Notification audio stream. Instead, the Ring audio stream will control both itself and the Notification stream.


In Conclusion

My goal with Audio Auto-adjust was to make a volume control app that fulfilled my needs. I hope that is as useful to others as it is to me. In the future I plan to expand the app with additional features, so please stay tuned!

Wednesday, November 02, 2011

code

Adventures in Android, Part IV: Vertical SeekBars

One user interface element that I wanted to include in my activities was a vertically-oriented SeekBar. While Android has provided View.setRotation() since API level 11, since I am targeting API level 7 I do not have that functionality available to me. I knew that this could probably be achievable by manually applying a transformation matrix to a View, but I had no idea how difficult it would end up being. I attempted numerous different approaches in an effort to achieve my desired results. Much to my frustration, I kept finding limitations in the APIs that made every option either unfeasible or difficult.

  1. My first attempt was to try something subclassing SeekBar similarly to this StackOverflow post. It overrides View.onDraw() and applies a transformation to the Canvas. It also sends fake size dimensions up to the SeekBar superclass and provides a custom View.onTouchEvent() handler. My concerns were as follows:
    • A custom event handler must call SeekBar.setProgress() to update the SeekBar's state. Since this is being done programmatically, any listeners will be told that this change did not come from the user, even though indirectly it actually did.
    • This custom event handler did not (and cannot) propagate touch events up to the SeekBar in a way that it will be able to redraw itself during the touch event. In particular, the SeekBar thumb was not being highlighted while the view was being touched.

  2. Figure 1: SeekBar with thumb drawn at incorrect location
    My second attempt was an extension to the first. Instead of completely overriding onTouchEvent(), I decided to use onTouchEvent() to perturb the touch coordinates of the provided MotionEvent, then call up into the superclass. While this fixed the issues from the first option, it still didn't look right.
    • Notice that in Figure 1, the SeekBar's progress indicator is at 100%, yet the thumb is located near the bottom. It turns out that the SeekBar's drawing code calls getWidth() to figure out where to position the thumb. Since the SeekBar is now in a vertical orientation, it should be using the height instead of the width.
  3. Finally we reach the third, definitive option: I wrote a derivative of ViewGroup called RotatedLayout that does a perfect transformation of the child View. From the child's perspective, it is operating using its regular orientation. The RotatedLayout class transforms coordinates for drawing, measuring, layout, invalidation, touch events and key events between its parent and its child. This allows me to provide my users with a pixel-perfect vertical SeekBar!
    • It was annoying to deal with invalidation; Android goes to great lengths to prevent you from tinkering with it. I had no choice, however: any invalidation rectangles generated by the SeekBar need to be transformed from the SeekBar's coordinate system to the parent view's coordinates. Figure 2 illustrates what happens if invalidation isn't transformed: Only a small region of pixels at the top of the SeekBar are redrawn. This happens because that small region happens to be exactly the same height as the underlying horizontal SeekBar. Figure 3 overlays a horizontal SeekBar with the misdrawn vertical SeekBar to illustrate.

Figure 2: Vertical SeekBar with no invalidation transformation

Figure 3: Invalidated region from Figure 2 overlaid with horizontal SeekBar

Figure 4: Vertical SeekBars in Audio Auto-adjust.
Are you interested in what RotatedLayout can do for your app? Drop me a line: ohmsblog at teamohms dot org

Wednesday, October 26, 2011

code

Adventures in Android, Part III

Today I want to talk about some architectural decisions that I made in the early stages of development that I hope will help to minimize the memory and battery consumption of my app.

First, let's discuss how I intended for my app to receive input. I do plan on having to interface directly with the user, so I'll be implementing a few activities. I also have a couple of BroadcastReceivers that I have added to my app's manifest in order to receive certain broadcast intents from the OS.

It very quickly became clear that I was going to want to implement a service to do my app's heavy lifting. Since my BroadcastReceiver was going to need to send intents over to the service via Context.startService(), I settled on using an IntentService so that my service would automatically stop itself when there were no more pending intents.

I also decided that it was going to be necessary for my service to communicate back to my activities. I decided that my activities will use Context.bindService() to facilitate this bi-directional communication. On the other hand, I don't want the service to be bound any longer than it reasonably needs to be.

Observe this snippet from the Android documentation on the service lifecycle:

A service can be both started and have connections bound to it. In such a case, the system will keep the service running as long as either it is started or there are one or more connections to it with the Context.BIND_AUTO_CREATE flag. Once neither of these situations hold, the service's onDestroy() method is called and the service is effectively terminated.

This information is important because it clarifies what happens if an IntentService tries to stop itself while the service is still bound: The service won't be destroyed until any connections are unbound.

After digesting all of this for a few moments, I came to the following conclusions:

  • My service will be implemented as an IntentService and it will process start intents sent by my activities and broadcast receivers. Unless any connections are bound to it, the service will stop itself as soon as it has processed all the intents in its queue.
  • My service will also support having connections bound to it. My activities will bind in order to achieve bi-directional communication with my service. Because of the guarantees made in the Android service lifecycle documentation, the IntentService implementation can't "pull the rug out" from under bound connections.
  • I will under no circumstances bind anything to the service unless the communication is essential to the functioning of my app. In other words, I'm not going to bind to the service and leave it running "just in case." In the case of my main activity, this means that I'll bind to the service in Activity.onStart() and unbind in Activity.onStop(); if the activity isn't visible and there aren't any start intents pending, then my service doesn't need to be consuming resources.
  • To compensate for the fact that the service is unlikely to be running continuously, I ensure that my activities send a refresh command to the service when they become visible. This gives the service an opportunity to update the activity as necessary to ensure that the UI is consistent with the service's internal state.

Monday, October 24, 2011

code

Adventures in Android, Part II

A brief annoyance that I was dealing with:

For testing purposes I wanted a service to initiate a status bar notification. I couldn't understand why, but for some bizarre reason Android kept failing miserably when trying to load the resource for my notification's icon. I kept seeing stuff in my ddms logs about android.content.res.Resources$NotFoundException.

I checked my code and my resources over and over again. I blew away the gen directory and rebuilt my app from scratch. I scoured StackOverflow for a situation similar to mine, yet nothing quite the same came up.

After pulling my hair for an hour or two, I inadvertently realized what the problem was.

I'm running CyanogenMod 7.1 on my phone. I had it set to forcibly install apps to my phone's SD card. Unfortunately for me, my app doesn't include any SD card support whatsoever.

*facepalm*

Problem solved: I moved the app into my phone's internal memory. I also decided to end my little experiment with forcing new installs onto the SD card.

Bloody hell!

Sunday, October 02, 2011

code

Adventures in Android, Part I

I've spent most of this afternoon and evening working on my first Android app.

I was dissatisfied with the current slate of applications that are available for a particular problem domain, so I have decided to write my own.

More details will follow as my work approaches completion, but in the meantime I'm going to jot down some thoughts about the stuff that I worked on today.

  • I haven't written a line of Java in probably seven or eight years because I think that it sucks. It wasn't difficult to get back into it, though I still don't like it.
  • I hate checked exceptions; they make the compiler annoying enough to motivate you to write suppressing catch blocks just so that your code will compile (Doing so is a bad idea, of course, so suck it up and do the right thing).
  • The APIs I wanted to use were not available publicly until Honeycomb. I want my app to run on Eclair. I ended up using reflection to give me the appropriate interfaces depending on availability: I use the documented API if it's available, but if I catch a NoSuchFieldException then I fall back to the undocumented APIs. I am amused that my first Android app, the one that I am writing to teach myself, is using undocumented APIs; I did the same thing when I started hacking 16-bit Windows code in 1995. It's actually a bit easier this time around because I may browse the entire source code. When I taught myself Windows I didn't have that luxury, though I always had my trusty copy of Undocumented Windows sitting nearby (I still have that book)!
  • I haven't used Eclipse since the last time that I wrote code in Java. This time around, I am proud to say that I successfully resisted using Eclipse for my Android work. I was worried that command-line environments were going to be an afterthought when it came to the Android developer tools but I was pleasantly surprised to be proven wrong. Vim and Ant FTW!
  • ddms is really cool.
  • Today's work passed testing! svn commit

<digression>This experience reinforces my opinion that hiring developers based on their ability to satisfy a laundry list of keywords is ridiculous. Good developers don't need a year to pick up whatever technologie du jour you're pushing. Whatever your buzzwords are, they'll probably be obsolete in five years anyway.</digression> That's another topic for another day.

Wednesday, June 22, 2011

CanadianPolitics musings

You're entitled to your opinion, but don't be hypocritical about it

In today's Globe and Mail, we have New Democrat MP Pat Martin suggesting that one effective mechanism for abolishing the Senate without amending the constitution would be to cut off its funding.

It's no secret that the NDP wants to abolish the Senate. While I don't agree with this option (and certainly don't agree with the hypocrisy surrounding minimum seat guarantees in the House), I certainly don't dispute that abolition is a legitimate alternative. What I do find interesting is that Mr. Martin's Senate reform strategy is quite similar to the Conservatives: Avoid amending the constitution, but adjust the parameters around which the Senate operates.

Hopefully while the Senate Reform Act is before the House, Mr. Martin will be cognizant of the fact that, while he may not agree with the reforms contained within those bills, he is a proponent of using parliament as the mechanism for reform. Were he to suddenly declare that a constitutional amendment is required, it would be nothing short of hypocritical.

Wednesday, May 25, 2011

privacy technology

Your unencrypted network traffic is vulnerable beyond the Wi-fi access point

Whether it's your Facebook identity or your Google Calendar traffic, there has been plenty of coverage lately about unencrypted information being sent to websites. What annoys the hell out of me, however, is this notion (coming from people who should know better, might I add) that these issues are only a problem if you're connecting to the internet via unsecured wi-fi.

This is complete and utter nonsense; the only thing that secured wi-fi gives you is a higher barrier to entry!

Consider this mental exercise: What happens to your data after it travels past the wi-fi access point? It needs to travel the remaining hundreds or thousands of kilometres to the website's data centre. If that information is unencrypted at the application layer (let's say via HTTP), it's just as visible by somebody intercepting it as it comes across the wire as it was over the unsecured airwaves. Wi-fi encryption is at the link layer, so it's only going to protect your data as it travels from your device to the access point; from then on, it's open season.

I don't dispute the fact that it's easier to snoop on unsecured wi-fi than it is to monitor a wired medium. What I do dispute is this ridiculous claim that securing your wireless connection or using a wired connection solves everything for you.

Avoiding unsecured wi-fi is not the solution. Using secure application layer protocols such as HTTPS is the solution.

Tuesday, May 03, 2011

code

Oracle OCI Tips

If OCIDirPathPrepare fails with ORA-01403, check and make sure that the OCI_ATTR_SCHEMA_NAME attribute has been set on the OCIDirPathCtx handle.

If OCIDirPathColArrayToStream fails with ORA-12899, you're probably not passing in the rowcnt and rowoff values correctly. The docs aren't very clear on how to specify those two parameters, so I'll try to elaborate here a little bit:

  • rowcnt should always be the number of rows that have been set in the column array, including rows that have already been sent to the stream buffer. A column array's set rows don't get cleared until you call OCIDirPathColArrayReset. If you specify a rowcnt that is too large (i.e. it includes unset rows), you may encounter errors.
  • rowoff should be the row index into the column array where stream conversion should begin.
If you follow these guidelines, OCIDirPathColArrayToStream will process rowcnt - rowoff rows during the conversion.

For example, let's suppose that you've previously converted three rows of a column array to the stream. You have just added a fourth row to the column array, and now you want to add that new row to the stream. Set rowcnt to 4, since that's the total number of rows that have been specified in the column array. Set rowoff to 3, since you've already converted rows 0, 1, and 2.

code

Fixing the winsock header file mess

Do you write Windows Sockets code? Are you having conflicts between winsock.h and winsock2.h?


If you take a look at winsock.h, you will notice that it uses the _WINSOCKAPI_ macro to prevent multiple inclusion. winsock2.h uses _WINSOCK2API_ to prevent multiple inclusion, but it also sets _WINSOCKAPI_ to fool the preprocessor into thinking that winsock.h has already been included.

Knowing this information, we can take this a step further and suppress winsock.h across the board (assuming Visual C++):

  1. First, modify your build system so that /D_WINSOCKAPI_ is always passed to the compiler. This makes the preprocessor think that winsock.h has already been included, so it never makes it past winsock.h's multiple inclusion preprocessor directives.
  2. Create a proxy header file using the following code snippet. Instead of including winsock2.h directly, always include the proxy header instead.
#ifndef __MYWINSOCK2_H
#define __MYWINSOCK2_H
#pragma push_macro("_WINSOCKAPI_")
// We clear _WINSOCKAPI_ to avoid preprocessor warnings about
// multiple definitions of the _WINSOCKAPI_ macro, as winsock2.h will
// attempt to #define _WINSOCKAPI_ itself.
#undef _WINSOCKAPI_
#include <winsock2.h>
#pragma pop_macro("_WINSOCKAPI_")
#endif // __MYWINSOCK2_H

Monday, April 25, 2011

code

How to generate your own sequential GUIDs for use with Microsoft SQL Server

Let's suppose that you want to create a table whose primary key is a uniqueidentifier and uses a clustered index. You may certainly use the NEWSEQUENTIALID() function to supply the next sequential GUID as a default. On the other hand, what do you do if you want to generate such a GUID in your application?


The answers are hidden in plain sight: The documentation for NEWSEQUENTIALID() states that this function wraps UuidCreateSequential(). At the same time, this blog post indicates that SQL Server slightly modifies the GUID before inserting the value as a default.

At this point, it's easy to see for yourself what SQL Server is doing to the GUID:

  1. Attach WinDbg to sqlservr.exe, enter bp rpcrt4!UuidCreateSequential, and resume the program.
  2. Insert a row into a table such that NEWSEQUENTIALID() will be invoked.
  3. When SQL Server triggers the breakpoint, enter the kb 1 command. Take note of the first argument to UuidCreateSequential().
  4. Enter the gu command.
  5. Type db address L16, where address is the argument from step 3. The output from this command is the raw bytes of the GUID structure that was generated.
  6. Resume the debugger.
  7. Query for the newly inserted row and inspect the GUID that SQL Server inserted into the database.
You'll find that the resulting GUID is identical to the GUID from step 5, except that the Data1, Data2, and Data3 fields have been converted to big-endian representation (RFC4122 actually recommends that GUIDs be big-endian encoded, but the Microsoft implementation uses little-endian fields).

Monday, October 18, 2010

Calgary

Calgary 2010 Civic Election

I am so proud to be a Calgarian tonight! Congratulations to Naheed Nenshi!

Wednesday, March 24, 2010

musings

Enough with the stereotyping, overgeneralizing headlines

First it was Sarah Palin, now it's Ann Coulter. It seems like as of late Calgary has been playing host to some of the most controversial right-wing lightning rods on the continent. Personally I can't believe that we're giving these people soapboxes to stand on. For me it's not so much about their ideas, it's about what they're using to back up their arguments: nothing of substance. Some of it is downright wrong. I don't think that these people should be censored (even The Globe and Mail agrees with that), but I am disappointed that they're being given a public platform on which to stand.

Having said that, let's take a look at the related headlines from The Globe and Mail:

Sarah Palin sees eye-to-eye with Albertans in Calgary speech


Spurned in Ottawa, Ann Coulter gets a big welcome from Calgary

I am embarrassed to be an Albertan when I see headlines like this. But why should I be? To read between the lines, you'd think that Calgary gave these women the keys to the city and threw parades for them that put the Stampede to shame. Why is it that a speech attended by a few hundred people gets distorted into some kind of city or province-wide euphoria? Note to The Globe's headline editor: as a Calgarian and as an Albertan, I do not see eye-to-eye with Sarah Palin, and I most certainly did not welcome Ann Coulter.

I find it ironic that the reason that Coulter was chased away from speaking at the University of Ottawa was because of fears that she might make inflammatory comments towards identifiable groups of people, and yet that's exactly the kind of thing I see people doing when making comments about Calgary and Alberta on The Globe's website. I'm not trying to be smug, but let's take a look at some of these comments from the Coulter article:


Calgary, Coulter, Conservatism! Wow! What a perfect match! If Stephen Harper had joined it would've been an icing on the cake.


Calgary and Coulter - a perfect match.


Of course Calgary would welcome her, there isn't a bigger bunch of american wannabes any were else in the world.Even their hockey team is an american reject and notice Alberta's oil is only for us markets. Yup she their kind of woman LOUD and STUPID. Yehaw


I'm sure Calgary will roll out the red carpet for Coulter.


Most of the people of Calgary are used to worshiping American oil companies, I'm sure they'll have no problem prostrating themselves at the feet of corporate America's fascist little fairy princess.


"Most of the people of Calgary...." Uh-huh. Sounds like this person is scooping "facts" out of the same latrine as Palin and Coulter. I'd laugh if any of these types of comments originated from the U of O. That would be deliciously hypocritical.

Friday, February 26, 2010

code

On the Risks of Fibers

Raymond Chen blogged about the risks of fibers today. I addressed these same concerns when writing about fibrous thread pools nearly two years ago. Raymond's post goes into a little bit more detail about why certain things are risky with fibers. I highly recommend it.

Saturday, December 05, 2009

code

Leaky Abstractions Redux, Part II

Last time I discussed why Winsock's connect function shouldn't be called by a thread running in the I/O component of the system thread pool. To recap, the implementation of connect does an alertable wait, causing APC work items that are enqueued to that thread to execute. This causes a cyclical effect where connect and the work items invoke each other repeatedly.

Another thread pool no-no is to call GetQueuedCompletionStatus from a thread in the default component. Instead of using the APC queue, the default component uses an I/O completion port as its queuing mechanism. This is actually very handy because I/O handles can be bound to the system thread pool using BindIoCompletionCallback. This allows asynchronous I/O completion events to be posted directly to the system thread pool.

I like to think of I/O completion ports as thread-safe queues provided by the OS that possess two unique properties:

  • Integration with the scheduler: When a thread that is associated with an I/O completion port blocks, the scheduler and the port cooperate so that another thread that is blocked on the port can be woken up and provided with an I/O completion packet.
  • Integration with the I/O subsystem: I/O completion packets can be posted directly to the port without any user-mode intervention.
Unfortunately only one I/O completion port can be associated with a thread at any given time. Because the default component of the system thread pool uses an I/O completion port for its queue, all WT_EXECUTEDEFAULT threads are already associated with a port. This means that if one of those threads blocks, the pool's port will know about it.

One associates a thread with an I/O completion port by calling GetQueuedCompletionStatus. The calling thread becomes associated with the port whose handle was passed in as the first parameter. This action overwrites any previous associations -- including the association that was made with the thread pool's port! This impairs the thread pool's ability to detect when one of its threads has blocked because the scheduler is no longer aware of the pool's completion port. In the best case, the thread pool will run less efficiently. In the worst case, a deadlock is possible.

Friday, December 04, 2009

musings

Food for thought

Why does the leader of the free world spend most of his time sequestered inside a house?

Thursday, December 03, 2009

code

!banned

If I banned myself from using every programming construct that could be abused, I'd be staring at a blank screen. Comments included.

Saturday, October 31, 2009

musings

The Top Ten Signs You're At a Bad Halloween Pub Crawl

Written by me as an accessory to my David Letterman costume:



  1. Blood is everywhere - and it's spraying out of a bite wound in your neck

  2. When you buy your ticket, the salesman asks, "Are you sure?"

  3. The guy sitting next to you is dressed up as Regis

  4. Bouncer demands proof that your flu shots are up to date

  5. Party games include such classics as "bobbing for assholes"

  6. Best costume award goes to wolfman wearing a slutty nurse outfit

  7. Your friends would rather stay home and discuss Balloon Boy on Twitter

  8. Bloody Mary you ordered comes with real blood

  9. Your bus captain pulls the bus over so he can change his Depends

  10. Some guy gets up and starts reciting a lame top ten list



(As an aside, I'd like to point out that I wrote this before Friday's Late Show. The topic of that show's top ten list? "Top Ten Signs You're At A Lame Halloween Party." There's even mention of Regis, bobbing for apples, H1N1 flu, Balloon Boy, blood, and "best costume." I've been watching way too much late night TV.)

Release 7.0; Copyright © 1996-2012 Aaron Klotz. All Rights Reserved.