tag:blogger.com,1999:blog-68482378295187451372024-03-12T18:26:04.585-06:00OhmsBlogAaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.comBlogger64125tag:blogger.com,1999:blog-6848237829518745137.post-55651667786900866442012-11-23T10:11:00.000-07:002012-11-23T10:11:37.441-07:00Announcing dblohm7.caOn 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 <a href="http://dblohm7.ca/" target="_blank">dblohm7.ca</a> and will be syndicated to <a href="http://planet.mozilla.org/" target="_blank">Planet Mozilla</a>.<br />
<br />
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.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-12416139228908408052012-06-17T22:51:00.000-06:002012-11-23T10:54:03.685-07:00How to switch a Cisco DPC3825 / EPC3825 to/from bridge mode at will<p class="notice" style="float:left; width:50%"><b>DISCLAIMER: This blog entry is unsupported.</b> 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, <i>please</i> do not post comments or send me emails asking me to solve your problem for you.</p>
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.<br />
<br />
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 <a href="http://bandaancha.eu/foros/conseguido-cisco-epc3825-modo-bridge-1675133">found</a> 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.<br />
<br />
On the forum website, their solution was to remove the <span style="font-family: 'Courier New', Courier, monospace;">display: none</span> 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 <span style="font-family: 'Courier New', Courier, monospace;"><select></span> tag, whose name was <span style="font-family: 'Courier New', Courier, monospace;">"working_mode"</span>! 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 <span style="font-family: 'Courier New', Courier, monospace;"><select></span> 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 <span style="font-family: 'Courier New', Courier, monospace;">"0"</span>, followed by "Bridged Only," implying a value of <span style="font-family: 'Courier New', Courier, monospace;">"1"</span>. 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<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;"><form name="administration" method="POST" action="/goform/Administration"></span><br />
<br />
tag and add the following:<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;"><input name="working_mode" type="hidden" value="1"></span><br />
<br />
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 <span style="font-family: 'Courier New', Courier, monospace;">192.168.100.1</span>. To switch back to router mode, do the same thing but set the input value to <span style="font-family: 'Courier New', Courier, monospace;">"0"</span>.<br />
<br />
<div class="blockcentered">
<img border="0" height="174" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiD9EU89sJq3hjz5xwRLUC5MijZ2pQ1a-jAT_HctLR7u-3ZxfO4bAeLeuMoWJ-azyYH-uT36a6r9e-zsUDdRPvBjxMHU607MwRUDA3WU-0YsphH0DPqt6MAWolhitmV0W-yNfSBU2sDtyw/s320/cisco_reset.png" width="320" /></div>
<br />
<br />
Of course you might want to script all of this and <span style="font-family: 'Courier New', Courier, monospace;">POST</span> the form directly.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com17tag:blogger.com,1999:blog-6848237829518745137.post-78848876778121630742012-03-14T17:10:00.000-06:002012-03-14T17:10:00.603-06:00How 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:<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">NT SERVICE\TrustedInstaller</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br />
</span><div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-34829195167984233532012-02-08T23:00:00.002-07:002012-06-06T20:58:15.262-06:00Announcing Audio Auto-adjustAs I've been chronicling in my <a href="http://ohmsblog.teamohms.org/2011/10/adventures-in-android-part-i.html">Adventures</a> <a href="http://ohmsblog.teamohms.org/2011/10/adventures-in-android-part-ii.html">in</a> <a href="http://ohmsblog.teamohms.org/2011/10/adventures-in-android-part-iii.html">Android</a> <a href="http://ohmsblog.teamohms.org/2011/11/adventures-in-android-part-iv.html">series</a>, I've been working on an Android app. I'm pleased to announce that today I have published <a href="https://market.android.com/details?id=com.ohmsoft.audioctl">Audio Auto-adjust 1.0.0</a> 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.<br />
<br />
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.<br />
<br />
<span style="font-size: large;">The Main Activity</span><br />
<br />
<img border="1" class="blockcentered" height="320" src="http://static.ohmsoft.com/img/portrait.png" width="192" /><br />
<br />
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.<br />
<br />
<br />
<img border="0" class="blockcentered" height="94" src="http://static.ohmsoft.com/img/toolbar-guide.png" width="480" /><br />
<br />
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.<br />
<br />
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.<br />
<br />
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.<br />
<br />
<img border="1" class="blockcentered" height="66" src="http://static.ohmsoft.com/img/menu.png" width="320" /><br />
<br />
<span style="font-size: large;">Edit Presets Activity</span><br />
<br />
<img border="1" class="blockcentered" height="320" src="http://static.ohmsoft.com/img/presets-activity.png" width="201" /><br />
<br />
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:<br />
<br />
<img border="1" class="blockcentered" height="320" src="http://static.ohmsoft.com/img/presets-context.png" width="192" /><br />
<br />
<span style="font-size: large;">Actions Activity</span><br />
<br />
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.<br />
<div class="pullquote">
<b>NOTE:</b> In order to configure Bluetooth events, you must enable Bluetooth on your phone before entering the "Configure Actions" activity.</div>
<br />
<img border="1" class="blockcentered" height="192" src="http://static.ohmsoft.com/img/actions.png" width="320" /><br />
<br />
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.<br />
<br />
<b>A Note About Precedence</b><br />
<br />
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.<br />
<br />
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.<br />
<br />
<span style="font-size: large;">Settings Activity</span><br />
<br />
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:<br />
<br />
<ul>
<li><b>Volume Control Stream:</b> 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.</li>
<li><b>Background Notification:</b> When enabled, Audio Auto-adjust will post a notification whenever it switches presets while running in the background.</li>
<li><b>Play Sounds:</b> When adjusting volume settings, play sounds to illustrate the loudness of the setting.</li>
<li><b>Tie Ring Volume:</b> When set, hide the controls for the Notification audio stream. Instead, the Ring audio stream will control both itself and the Notification stream.</li>
</ul>
<br />
<br />
<span style="font-size: large;">In Conclusion</span><br />
<br />
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!<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-41616631423422900222011-11-02T07:00:00.019-06:002012-06-06T21:20:23.196-06:00Adventures in Android, Part IV: Vertical SeekBarsOne user interface element that I wanted to include in my activities was a vertically-oriented <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a href="http://developer.android.com/reference/android/widget/SeekBar.html">SeekBar</a></span>. While Android has provided <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a href="http://developer.android.com/reference/android/view/View.html#setRotation(float)">View.setRotation()</a></span> 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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">View</span>, 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.<br />
<div><ol class="padded"><li>My first attempt was to try something subclassing <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span> similarly to <a href="http://stackoverflow.com/questions/4892179/need-working-vertical-seekbar">this StackOverflow post</a>. It overrides <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a href="http://developer.android.com/reference/android/view/View.html#onDraw(android.graphics.Canvas)">View.onDraw()</a></span> and applies a transformation to the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Canvas</span>. It also sends fake size dimensions up to the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span> superclass and provides a custom <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a href="http://developer.android.com/reference/android/view/View.html#onTouchEvent(android.view.MotionEvent)">View.onTouchEvent()</a></span> handler. My concerns were as follows:<br />
<ul><li>A custom event handler must call <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar.setProgress()</span> to update the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span>'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.</li>
<li>This custom event handler did not (and cannot) propagate touch events up to the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span> in a way that it will be able to redraw itself during the touch event. In particular, the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span> thumb was not being highlighted while the view was being touched.</li>
</ul></li>
<li><div class="pullquote" style="width: 100px;"><img border="0" class="blockcentered" height="176" src="http://static.ohmsoft.com/img/SeekBarBadThumb.png" width="22" /><br />
<span class="Apple-style-span" style="font-size: x-small;"><b>Figure 1</b>: <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span> with thumb drawn at incorrect location</span></div>My second attempt was an extension to the first. Instead of completely overriding <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">onTouchEvent()</span>, I decided to use <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">onTouchEvent()</span> to perturb the touch coordinates of the provided <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a href="http://developer.android.com/reference/android/view/MotionEvent.html">MotionEvent</a></span>, then call up into the superclass. While this fixed the issues from the first option, it still didn't look right.<br />
<ul><li>Notice that in Figure 1, the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span>'s progress indicator is at 100%, yet the thumb is located near the bottom. It turns out that the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span>'s drawing code calls <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">getWidth()</span> to figure out where to position the thumb. Since the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span> is now in a vertical orientation, it should be using the height instead of the width.</li>
</ul></li>
<li>Finally we reach the third, definitive option: I wrote a derivative of <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a href="http://developer.android.com/reference/android/view/ViewGroup.html">ViewGroup</a></span> called <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">RotatedLayout</span> that does a perfect transformation of the child <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">View</span>. From the child's perspective, it is operating using its regular orientation. The <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">RotatedLayout</span> 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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span>!</li>
<ul><li>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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span> need to be transformed from the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span>'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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span> 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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span> with the misdrawn vertical <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span> to illustrate.</li>
</ul></ol><div class="blockcentered" style="width: 570px;"><div style="float: left; padding-right: 50px; width: 150px;"><img border="0" class="blockcentered" height="320" src="http://static.ohmsoft.com/img/SeekBarBadInvalidate.png" width="40" /><br />
<b>Figure 2:</b> Vertical <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span> with no invalidation transformation</div><div style="float: left; padding-left: 50px; width: 320px;"><img border="0" class="blockcentered" height="320" src="http://static.ohmsoft.com/img/SeekBarOverlay.png" width="319" /><br />
<b>Figure 3:</b> Invalidated region from Figure 2 overlaid with horizontal <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span></div></div><div class="cboth"></div><div class="blockcentered" style="padding-top: 50px; padding-bottom: 50px; width: 400px; text-align: center;"><img border="1" class="blockcentered" width="400" height="240" src="http://static.ohmsoft.com/img/landscape-various.png" /><br />
<b>Figure 4:</b> Vertical <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">SeekBar</span>s in <a href="https://market.android.com/details?id=com.ohmsoft.audioctl">Audio Auto-adjust</a>.<br />
</div></div>Are you interested in what <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">RotatedLayout</span> can do for your app? Drop me a line: ohmsblog at teamohms dot org<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com2tag:blogger.com,1999:blog-6848237829518745137.post-49859516120959889542011-10-26T07:00:00.001-06:002011-10-26T07:00:14.276-06:00Adventures in Android, Part IIIToday 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.<br />
<br />
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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a href="http://developer.android.com/reference/android/content/BroadcastReceiver.html">BroadcastReceiver</a></span>s that I have added to my app's manifest in order to receive certain broadcast intents from the OS.<br />
<br />
It very quickly became clear that I was going to want to implement a service to do my app's heavy lifting. Since my <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">BroadcastReceiver</span> was going to need to send intents over to the service via <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a href="http://developer.android.com/reference/android/content/Context.html#startService(android.content.Intent)">Context.startService()</a></span>, I settled on using an <a href="http://developer.android.com/reference/android/app/IntentService.html"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">IntentService</span></a> so that my service would automatically stop itself when there were no more pending intents.<br />
<br />
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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a href="http://developer.android.com/reference/android/content/Context.html#bindService(android.content.Intent, android.content.ServiceConnection, int)">Context.bindService()</a></span> 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.<br />
<br />
Observe this snippet from the Android documentation on the <a href="http://developer.android.com/reference/android/app/Service.html#ServiceLifecycle">service lifecycle</a>:<br />
<br />
<blockquote>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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Context.BIND_AUTO_CREATE</span> flag. Once neither of these situations hold, the service's <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">onDestroy()</span> method is called and the service is effectively terminated.</blockquote><br />
This information is important because it clarifies what happens if an <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">IntentService</span> tries to stop itself while the service is still bound: The service won't be destroyed until any connections are unbound.<br />
<br />
After digesting all of this for a few moments, I came to the following conclusions:<br />
<br />
<ul><li>My service will be implemented as an <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">IntentService</span> 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.</li>
<li>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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">IntentService</span> implementation can't "pull the rug out" from under bound connections.</li>
<li>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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a href="http://developer.android.com/reference/android/app/Activity.html#onStart()">Activity.onStart()</a></span> and unbind in <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><a href="http://developer.android.com/reference/android/app/Activity.html#onStop()">Activity.onStop()</a></span>; if the activity isn't visible and there aren't any start intents pending, then my service doesn't need to be consuming resources.</li>
<li>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.</li>
</ul><div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-40131649964358794292011-10-24T07:00:00.002-06:002011-10-24T08:10:12.261-06:00Adventures in Android, Part IIA brief annoyance that I was dealing with:<br />
<br />
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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ddms</span> logs about <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">android.content.res.Resources$NotFoundException</span>.<br />
<br />
I checked my code and my resources over and over again. I blew away the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">gen</span> directory and rebuilt my app from scratch. I scoured StackOverflow for a situation similar to mine, yet nothing quite the same came up.<br />
<br />
After pulling my hair for an hour or two, I inadvertently realized what the problem was.<br />
<br />
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.<br />
<br />
*facepalm*<br />
<br />
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.<br />
<br />
Bloody hell!<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-43213440140507548842011-10-02T21:46:00.004-06:002011-10-03T01:12:31.464-06:00Adventures in Android, Part II've spent most of this afternoon and evening working on my first Android app.<br />
<br />
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.<br />
<br />
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.<br />
<br />
<ul><li>I haven't written a line of Java in probably seven or eight years because <a href="http://www.mathnews.uwaterloo.ca/Issues/mn9402/javasucks.php">I think that it sucks</a>. It wasn't difficult to get back into it, though I still don't like it.</li>
<li>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).</li>
<li>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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">NoSuchFieldException</span> 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 <i><a href="http://www.amazon.ca/gp/product/0201608340/ref=as_li_ss_tl?ie=UTF8&tag=ohmsblog-20&linkCode=as2&camp=15121&creative=390961&creativeASIN=0201608340">Undocumented Windows</a></i> sitting nearby (I still have that book)!</li>
<li>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!</li>
<li><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ddms</span> is really cool.</li>
<li>Today's work passed testing! <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">svn commit</span></li>
</ul><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><digression></span>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 <i>technologie du jour</i> you're pushing. Whatever your buzzwords are, they'll probably be obsolete in five years anyway.<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></digression></span> That's another topic for another day.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-59108859194168542092011-06-22T13:14:00.003-06:002011-06-22T14:39:21.639-06:00You're entitled to your opinion, but don't be hypocritical about itIn today's <i>Globe and Mail</i>, we have New Democrat MP Pat Martin suggesting that one effective mechanism for abolishing the Senate without amending the constitution would be to <a href="http://www.theglobeandmail.com/news/politics/ottawa-notebook/if-harper-cant-hobble-or-kill-it-ndp-hopes-to-bleed-the-senate-dry/article2070579/">cut off its funding</a>.<br />
<br />
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.<br />
<br />
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.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-61927412645199026232011-05-25T13:19:00.004-06:002011-05-25T23:52:05.456-06:00Your unencrypted network traffic is vulnerable beyond the Wi-fi access pointWhether 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.<br /><br />This is complete and utter nonsense; the only thing that secured wi-fi gives you is a higher barrier to entry!<br /><br />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.<br /><br />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.<br /><br />Avoiding unsecured wi-fi is not the solution. Using secure application layer protocols such as HTTPS is the solution.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com1tag:blogger.com,1999:blog-6848237829518745137.post-71056887267109446632011-05-03T23:56:00.006-06:002011-05-18T16:00:48.100-06:00Oracle OCI TipsIf <tt>OCIDirPathPrepare</tt> fails with <tt>ORA-01403</tt>, check and make sure that the <tt><a href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14250/ociaahan.htm#sthref6250">OCI_ATTR_SCHEMA_NAME</a></tt> attribute has been set on the <tt>OCIDirPathCtx</tt> handle.<br /><br />If <tt>OCIDirPathColArrayToStream</tt> fails with <tt>ORA-12899</tt>, you're probably not passing in the <tt>rowcnt</tt> and <tt>rowoff</tt> values correctly. The <a href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14250/oci16msc004.htm#sthref3214">docs</a> aren't very clear on how to specify those two parameters, so I'll try to elaborate here a little bit:<br /><ul><li><tt>rowcnt</tt> should always be the number of rows that have been <i>set</i> 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 <tt>OCIDirPathColArrayReset</tt>. If you specify a <tt>rowcnt</tt> that is too large (i.e. it includes unset rows), you may encounter errors.</li><li><tt>rowoff</tt> should be the row index into the column array where stream conversion should begin.</li></ul><div>If you follow these guidelines, <tt>OCIDirPathColArrayToStream</tt> will process <tt>rowcnt - rowoff</tt> rows during the conversion.</div><div><br /></div><div>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 <tt>rowcnt</tt> to 4, since that's the total number of rows that have been specified in the column array. Set <tt>rowoff</tt> to 3, since you've already converted rows 0, 1, and 2.</div><div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-4888555141916383792011-05-03T23:28:00.004-06:002011-05-04T00:30:11.364-06:00Fixing the winsock header file messDo you write Windows Sockets code? Are you having conflicts between <span style="font-family:courier new;">winsock.h</span> and <span style="font-family:courier new;">winsock2.h</span>?<div><br /></div><div>If you take a look at <span style="font-family:courier new;">winsock.h</span>, you will notice that it uses the <span style="font-family:courier new;">_WINSOCKAPI_</span> macro to prevent multiple inclusion. <span style="font-family:courier new;">winsock2.h</span> uses <span style="font-family:courier new;">_WINSOCK2API_</span> to prevent multiple inclusion, but it also sets <span style="font-family:courier new;">_WINSOCKAPI_</span> to fool the preprocessor into thinking that <span style="font-family:courier new;">winsock.h</span> has already been included.</div><div><br /></div><div>Knowing this information, we can take this a step further and suppress <span style="font-family:courier new;">winsock.h</span> across the board (assuming Visual C++):</div><div><br /></div><div><ol><li>First, modify your build system so that <span style="font-family:courier new;">/D_WINSOCKAPI_</span> is always passed to the compiler. This makes the preprocessor think that <span style="font-family:courier new;">winsock.h</span> has already been included, so it never makes it past <span style="font-family:courier new;">winsock.h</span>'s multiple inclusion preprocessor directives.</li><li>Create a proxy header file using the following code snippet. Instead of including <span style="font-family:courier new;">winsock2.h</span> directly, always include the proxy header instead.</li></ol></div><div><blockquote><span style="font-family:courier new;"><nobr>#ifndef __MYWINSOCK2_H</nobr><br /><nobr>#define __MYWINSOCK2_H</nobr><br /><nobr>#pragma push_macro("_WINSOCKAPI_")</nobr><br /><nobr>// We clear _WINSOCKAPI_ to avoid preprocessor warnings about</nobr><br /><nobr>// multiple definitions of the _WINSOCKAPI_ macro, as winsock2.h will</nobr><br /><nobr>// attempt to #define _WINSOCKAPI_ itself.</nobr><br /><nobr>#undef _WINSOCKAPI_</nobr><br /><nobr>#include <winsock2.h></nobr><br /><nobr>#pragma pop_macro("_WINSOCKAPI_")</nobr><br /><nobr>#endif // __MYWINSOCK2_H</nobr></span></blockquote></div><div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com2tag:blogger.com,1999:blog-6848237829518745137.post-15026504859939862412011-04-25T21:45:00.002-06:002011-04-27T02:27:04.024-06:00How to generate your own sequential GUIDs for use with Microsoft SQL ServerLet's suppose that you want to create a table whose primary key is a <span style="font-family:courier new;"><a href="http://msdn.microsoft.com/en-us/library/ms187942(v=SQL.105).aspx">uniqueidentifier</a></span> and uses a clustered index. You may certainly use the <span style="font-family:courier new;"><a href="http://msdn.microsoft.com/en-us/library/ms189786(v=SQL.105).aspx">NEWSEQUENTIALID()</a></span> 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?<div><br /></div><div>The answers are hidden in plain sight: The <a href="http://msdn.microsoft.com/en-us/library/ms189786(v=SQL.105).aspx">documentation</a> for <span style="font-family:courier new;">NEWSEQUENTIALID()</span> states that this function wraps <span style="font-family:courier new;"><a href="http://msdn.microsoft.com/en-us/library/aa379322(VS.85).aspx">UuidCreateSequential()</a></span>. At the same time, <a href="http://blogs.msdn.com/b/sqlprogrammability/archive/2006/03/23/559061.aspx">this blog post</a> indicates that SQL Server slightly modifies the GUID before inserting the value as a default.</div><div><br /></div><div>At this point, it's easy to see for yourself what SQL Server is doing to the GUID:</div><div><br /></div><div><ol><li>Attach WinDbg to sqlservr.exe, enter <span style="font-family:courier new;">bp rpcrt4!UuidCreateSequential</span>, and resume the program.</li><li>Insert a row into a table such that <span style="font-family:courier new;">NEWSEQUENTIALID()</span> will be invoked.</li><li>When SQL Server triggers the breakpoint, enter the <span style="font-family:courier new;">kb 1</span> command. Take note of the first argument to <span style="font-family:courier new;">UuidCreateSequential()</span>.</li><li>Enter the <span style="font-family:courier new;">gu</span> command.</li><li>Type <span style="font-family:courier new;">db <i>address</i> L16</span>, where <i>address</i> is the argument from step 3. The output from this command is the raw bytes of the <a href="http://msdn.microsoft.com/en-us/library/aa379358(v=vs.85).aspx"><span style="font-family:courier new;">GUID</span> structure</a> that was generated.</li><li>Resume the debugger.</li><li>Query for the newly inserted row and inspect the GUID that SQL Server inserted into the database.</li></ol><div>You'll find that the resulting GUID is identical to the GUID from step 5, except that the <span style="font-family:courier new;">Data1</span>, <span style="font-family:courier new;">Data2</span>, and <span style="font-family:courier new;">Data3</span> fields have been converted to big-endian representation (<span style="font-family:courier new;"><a href="http://www.ietf.org/rfc/rfc4122.txt">RFC4122</a></span> actually recommends that GUIDs be big-endian encoded, but the Microsoft implementation uses little-endian fields).</div></div><div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-53978742526968046702010-10-18T23:23:00.002-06:002011-01-17T23:54:42.518-07:00Calgary 2010 Civic ElectionI am so proud to be a Calgarian tonight! Congratulations to Naheed Nenshi!<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-10279380978786578152010-03-24T22:26:00.005-06:002010-03-24T22:56:19.065-06:00Enough with the stereotyping, overgeneralizing headlinesFirst 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 <a href="http://www.youtube.com/watch?v=Vg7IhR0ccgo">wrong</a>. I don't think that these people should be censored (even <span style="font-style: italic;">The Globe and Mail</span> <a href="http://tgam.ca/K5A">agrees</a> with that), but I am disappointed that they're being given a public platform on which to stand.<br /><br />Having said that, let's take a look at the related headlines from <span style="font-style: italic;">The Globe and Mail</span>:<br /><a href="http://tgam.ca/J0G"><br />Sarah Palin sees eye-to-eye with Albertans in Calgary speech</a><br /><br /><a href="http://tgam.ca/K4p">Spurned in Ottawa, Ann Coulter gets a big welcome from Calgary</a><br /><br />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.<br /><br />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:<br /><blockquote><br />Calgary, Coulter, Conservatism! Wow! What a perfect match! If Stephen Harper had joined it would've been an icing on the cake. </blockquote><br /><blockquote><br />Calgary and Coulter - a perfect match. </blockquote><br /><br /><blockquote>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 </blockquote><br /><br /><blockquote>I'm sure Calgary will roll out the red carpet for Coulter.</blockquote><br /><br /><blockquote>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. </blockquote><br /><br />"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.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-11621374886957739222010-02-26T13:19:00.002-07:002010-02-26T13:22:56.783-07:00On the Risks of FibersRaymond Chen blogged about the risks of fibers today. I addressed these same concerns when writing about <a href="http://ohmsblog.teamohms.org/2008/05/fibrous-thread-pools.html">fibrous thread pools</a> nearly two years ago. Raymond's <a href="http://blogs.msdn.com/oldnewthing/archive/2010/02/26/9969664.aspx">post</a> goes into a little bit more detail about why certain things are risky with fibers. I highly recommend it.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-61327899753051953362009-12-05T12:30:00.005-07:002011-01-18T08:40:05.409-07:00Leaky Abstractions Redux, Part II<a href="http://ohmsblog.teamohms.org/2009/10/leaky-abstractions-redux-part-i.html">Last time</a> I discussed why Winsock's <span style="font-family:courier new;">connect</span> function shouldn't be called by a thread running in the I/O component of the system thread pool. To recap, the implementation of <span class="Apple-style-span" >connect</span> does an alertable wait, causing APC work items that are enqueued to that thread to execute. This causes a cyclical effect where <span style="font-family:courier new;">connect</span> and the work items invoke each other repeatedly.<br /><br />Another thread pool no-no is to call <a href="http://msdn.microsoft.com/en-us/library/aa364986%28VS.85%29.aspx"><span style="font-family:courier new;">GetQueuedCompletionStatus</span></a> 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 <a href="http://msdn.microsoft.com/en-us/library/aa363484%28VS.85%29.aspx"><span style="font-family:courier new;">BindIoCompletionCallback</span></a>. This allows asynchronous I/O completion events to be posted directly to the system thread pool.<br /><br />I like to think of I/O completion ports as thread-safe queues provided by the OS that possess two unique properties:<br /><br /><ul><li>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.<br /></li><li>Integration with the I/O subsystem: I/O completion packets can be posted directly to the port without any user-mode intervention.</li></ul>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, <span style="font-weight: bold;">all <span style="font-family:courier new;">WT_EXECUTEDEFAULT</span> threads are already associated with a port</span>. This means that if one of those threads blocks, the pool's port will know about it.<br /><br />One associates a thread with an I/O completion port by calling <span style="font-family:courier new;">GetQueuedCompletionStatus</span>. 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.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-92213745245207506172009-12-04T23:34:00.002-07:002009-12-04T23:34:47.574-07:00Food for thoughtWhy does the leader of the free world spend most of his time sequestered inside a house?<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-59350260656227460542009-12-03T19:35:00.004-07:002009-12-05T12:14:35.263-07:00!bannedIf I banned myself from using every programming construct that could be abused, I'd be staring at a blank screen. Comments included.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-20653991009141822792009-10-31T22:00:00.011-06:002012-06-06T21:25:11.779-06:00The Top Ten Signs You're At a Bad Halloween Pub CrawlWritten by me as an accessory to my David Letterman costume:<br /><br /><div class="centered"><img src="http://static.teamohms.org/img/dave.jpg" height="216" width="150" /></div><br /><ol class="topten"><br /><li>Blood is everywhere - and it's spraying out of a bite wound in your neck</li><br /><li>When you buy your ticket, the salesman asks, "Are you sure?"</li><br /><li>The guy sitting next to you is dressed up as <a href="http://en.wikipedia.org/wiki/Regis_Philbin">Regis</a></li><br /><li>Bouncer demands proof that your flu shots are up to date</li><br /><li>Party games include such classics as "bobbing for assholes"</li><br /><li>Best costume award goes to wolfman wearing a slutty nurse outfit</li><br /><li>Your friends would rather stay home and discuss Balloon Boy on Twitter</li><br /><li>Bloody Mary you ordered comes with real blood</li><br /><li>Your bus captain pulls the bus over so he can change his Depends</li><br /><li>Some guy gets up and starts reciting a lame top ten list</li><br /></ol><br /><br /><span style="font-size:78%;">(As an aside, I'd like to point out that I wrote this before Friday's <span style="font-style: italic;">Late Show</span>. The topic of that show's top ten list? "<a href="http://www.cbs.com/late_night/late_show/top_ten/top_ten_popup.php?origin=Late+Show+Top+Ten&year=2009&month=10&day=30&elm=0&cat=content_top_ten">Top Ten Signs You're At A Lame Halloween Party</a>." 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.)</span><div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-58678848253522233242009-10-07T20:30:00.015-06:002009-12-05T12:14:05.069-07:00Leaky Abstractions Redux, Part IToday at work I was stuck fixing a stubborn deadlock between two multithreaded services on Windows that were communicating with each other over TCP/IP. This problem ended up being another instance of what Joel Spolsky refers to as <a href="http://www.joelonsoftware.com/articles/LeakyAbstractions.html">Leaky Abstractions</a>.<br /><br />To understand what is happening here, I need to sketch out a bit of background. The first item to note is that the server process uses an M:N threading model, as opposed to a 1:1 threading model. M connections are multiplexed onto N threads, instead of creating one thread per connection. Because N does not increase without bound, it could be possible for those threads to be exhausted under pathological conditions.<br /><br />The second noteworthy point is that the threads that are initiating the connections on the client side come from the I/O component of the system thread pool. That is, the TCP/IP client was being queued up onto the system thread pool by calling <a href="http://msdn.microsoft.com/en-us/library/ms684957%28VS.85%29.aspx"><span style="font-family:courier new;">QueueUserWorkItem</span></a> with the <span style="font-family:courier new;">WT_EXECUTEINIOTHREAD</span> flag. The I/O component of the system thread pool uses user-mode <a href="http://msdn.microsoft.com/en-us/library/ms681951%28VS.85%29.aspx">asynchronous procedure calls</a> (APCs) as the queuing mechanism. When the pool thread needs another work item, it performs an <a href="http://msdn.microsoft.com/en-us/library/ms687069%28VS.85%29.aspx#alertable_wait_functions">alertable wait</a> until the next APC arrives. Since there is only one APC queue per thread, APCs can come from multiple sources but arrive on this single queue in some arbitrary order. If there are multiple sources, we can't guarantee which APC actually gets called when the thread goes alertable; whatever's at the head of the queue is what gets invoked.<br /><br />That's enough background, so let's take a look at the deadlocked client process. Once I had WinDbg attached, I noticed that the call stack for the thread pool's I/O component looked something like this:<br /><br /><span style="font-family:courier new;">ntdll!KiFastSystemCallRet</span><br /><span style="font-family:courier new;">ntdll!NtWaitForSingleObject+0xc</span><br /><span style="font-family:courier new;">kernel32!WaitForSingleObjectEx+0xac</span><br /><span style="font-family:courier new;">kernel32!WaitForSingleObject+0x12</span><br /><span style="font-family:courier new;">myprog!MyHandshake+0x86</span><br /><span style="font-family:courier new;">myprog!MyWorkItem+0x3a</span><br /><span style="font-family:courier new;">ntdll!RtlpWorkerCallout+0x71</span><br /><span style="font-family:courier new;">ntdll!RtlpExecuteIOWorkItem+0x29</span><br /><span style="font-family:courier new;">ntdll!KiUserApcDispatcher+0x25</span><br /><span style="font-family:courier new;">mswsock!SockDoConnectReal+0x27a</span><br /><span style="font-family:courier new;">mswsock!SockDoConnect+0x38a</span><br /><span style="font-family:courier new;">mswsock!WSPConnect+0xbe</span><br /><span style="font-family:courier new;">WS2_32!connect+0x52</span><br /><span style="font-family:courier new;">myprog!MyWorkItem+0x3a</span><br /><span style="font-family:courier new;">ntdll!RtlpWorkerCallout+0x71</span><br /><span style="font-family:courier new;">ntdll!RtlpExecuteIOWorkItem+0x29</span><br /><span style="font-family:courier new;">ntdll!KiUserApcDispatcher+0x25</span><br /><span style="font-family:courier new;">mswsock!SockDoConnectReal+0x27a</span><br /><span style="font-family:courier new;">mswsock!SockDoConnect+0x38a</span><br /><span style="font-family:courier new;">mswsock!WSPConnect+0xbe</span><br /><span style="font-family:courier new;">WS2_32!connect+0x52</span><br /><span style="font-family:courier new;">myprog!MyWorkItem+0x3a</span><br /><span style="font-family:courier new;">ntdll!RtlpWorkerCallout+0x71</span><br /><span style="font-family:courier new;">ntdll!RtlpExecuteIOWorkItem+0x29</span><br /><span style="font-family:courier new;">ntdll!KiUserApcDispatcher+0x25</span><br /><span style="font-family:courier new;">mswsock!SockDoConnectReal+0x27a</span><br /><span style="font-family:courier new;">mswsock!SockDoConnect+0x38a</span><br /><span style="font-family:courier new;">mswsock!WSPConnect+0xbe</span><br /><span style="font-family:courier new;">WS2_32!connect+0x52</span><br /><span style="font-family:courier new;">myprog!MyWorkItem+0x3a</span><br /><span style="font-family:courier new;">ntdll!RtlpWorkerCallout+0x71</span><br /><span style="font-family:courier new;">ntdll!RtlpExecuteIOWorkItem+0x29</span><br /><span style="font-family:courier new;">ntdll!KiUserApcDispatcher+0x25</span><br /><span style="font-family:courier new;">mswsock!SockDoConnectReal+0x27a</span><br /><span style="font-family:courier new;">mswsock!SockDoConnect+0x38a</span><br /><span style="font-family:courier new;">mswsock!WSPConnect+0xbe</span><br /><span style="font-family:courier new;">WS2_32!connect+0x52</span><br /><span style="font-family:courier new;">myprog!MyWorkItem+0x3a</span><br /><span style="font-family:courier new;">ntdll!RtlpWorkerCallout+0x71</span><br /><span style="font-family:courier new;">ntdll!RtlpExecuteIOWorkItem+0x29</span><br /><span style="font-family:courier new;">ntdll!KiUserApcDispatcher+0x25</span><br /><span style="font-family:courier new;">mswsock!SockDoConnectReal+0x27a</span><br /><span style="font-family:courier new;">mswsock!SockDoConnect+0x38a</span><br /><span style="font-family:courier new;">mswsock!WSPConnect+0xbe</span><br /><span style="font-family:courier new;">WS2_32!connect+0x52</span><br /><span style="font-family:courier new;">myprog!MyWorkItem+0x3a</span><br /><span style="font-family:courier new;">ntdll!RtlpWorkerCallout+0x71</span><br /><span style="font-family:courier new;">ntdll!RtlpExecuteIOWorkItem+0x29</span><br /><span style="font-family:courier new;">ntdll!KiUserApcDispatcher+0x25</span><br /><span style="font-family:courier new;">mswsock!SockDoConnectReal+0x27a</span><br /><span style="font-family:courier new;">mswsock!SockDoConnect+0x38a</span><br /><span style="font-family:courier new;">mswsock!WSPConnect+0xbe</span><br /><span style="font-family:courier new;">WS2_32!connect+0x52</span><br /><span style="font-family:courier new;">myprog!MyWorkItem+0x3a</span><br /><span style="font-family:courier new;">ntdll!RtlpWorkerCallout+0x71</span><br /><span style="font-family:courier new;">ntdll!RtlpExecuteIOWorkItem+0x29</span><br /><span style="font-family:courier new;">ntdll!KiUserApcDispatcher+0x25</span><br /><br />Notice that there are seven invocations of <span style="font-family:courier new;">myprog!MyWorkItem</span> on the call stack. Further notice that the call stack contains a pattern that repeats itself after every invocation of <span style="font-family:courier new;">mswsock!SockDoConnectReal</span>. Of course Winsock has no knowledge of my code, so it can't be intentionally invoking my work item. As soon as I saw this I knew what it meant: the internals of the Winsock <span style="font-family:courier new;">connect</span> API are implemented using APCs! Since both my code and Winsock were queuing up APCs to this thread, Winsock invoked whichever procedure was at the head of the APC queue when it went alertable. In this case it was my work item instead of the internal Winsock procedure. My work item then attempts another connect, thus repeating the cycle. Since all of the connections on the stack had only been partially completed, this gobbled up resources on the server side until there were no threads available to service the handshake at the top of the call stack. Talk about pathological conditions: there's our deadlock!<br /><br />I ended up removing the offending client-side code from the system thread pool altogether. You might be wondering why that code was even using the I/O component of the thread pool in the first place. Why didn't it just use <span style="font-family:courier new;">WT_EXECUTEDEFAULT</span>? There is a good reason for this, and ironically enough it too is because of a leaky abstraction! That tale will have to wait until <a href="http://ohmsblog.teamohms.org/2009/05/leaky-abstractions-redux-part-ii.html">Part II</a>.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-78578110449732447062009-09-13T11:39:00.013-06:002009-09-13T15:25:25.439-06:00SatisfactionIn Alberta I always encounter a bit of hostility when somebody learns about which school is my <a href="http://www.uwaterloo.ca/"><span style="font-style: italic;">alma mater</span></a>. One guy once told me that he'd intentionally hire employees from any institution but mine, regardless of technical merit, simply out of spite.<br /><br />This is rather frustrating for me, because while I was in Waterloo I encountered hostility for being an Albertan. One interviewer at a major smartphone manufacturer told me when the interview began that he "wouldn't hold it against me" for being an Albertan.<br /><br />Either way, it seems like I can't win. Except for this one time:<br /><br />In 2002 I decided to take a break from my co-op program and spend the summer at home with my friends and family. I took a job with my local municipal government to pay the bills.<br /><br />One day, one of the IT guys walked into the office that I shared with another summer student. He hadn't met me yet, so my office mate introduced me to him. As soon as he found out where I was going to school, he started tearing into me. "So what on earth are you doing back here?" he asked.<br /><br />After I explained why I came home, he made some kind of snide, slightly offensive remark. Noticing that I was not exactly impressed, he then looked at my office mate and said, "I guess he doesn't have a sense of humour."<br /><br />I glared at him and retorted, "Maybe you're not funny."<br /><br />I must have caught him off guard, because he frowned and promptly left. For the rest of the summer I was stuck having to do my own IT support. The satisfaction that I felt made it entirely worthwhile.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-90727555013715755122009-06-25T09:43:00.010-06:002009-06-25T11:25:06.206-06:00Review: Transformers: Revenge of The Fallen (2009) (SPOILERS)I'm no professional critic, but as an occasional student of film and as an avid movie watcher I've learned enough about criticism to know that everything must be taken into context. I'm not about to compare a <span style="font-style: italic;">Transformers</span> movie to <a href="http://www.imdb.com/title/tt0033467/"><span style="font-style: italic;">Citizen Kane</span> (1941)</a>. On the other hand, comparing this movie to other films in the same genre is fair game. I loved <a href="http://www.imdb.com/title/tt0117500/"><span style="font-style: italic;">The Rock</span> (1996)</a>, so it's not like I'm inherently anti-Michael Bay.<br /><br />Being fairly knowledgeable about the <span style="font-style: italic;">Transformers</span> universe as a whole (I've seen and own every single episode of the original animated series), I think I've got a pretty good background for critisizing <a href="http://www.imdb.com/title/tt1055369/"><span style="font-style: italic;">Transformers: Revenge of The Fallen</span> (2009)</a>.<br /><br />On a gut-feel level, I thought that the movie was okay. Not spectacular, but okay. The previous film had me shaking in my seat upon hearing Peter Cullen's voice as Optimus Prime for the first time in 20 years. I enjoyed seeing my favourite characters from my childhood brought alive in a live-action picture. Walking out of the theatre this time around, I can't really say that I was as excited.<br /><br />First I'll describe the parts of the film that I did like, in order of preference:<br /><ol><li>Prime engaged in hand-to-hand combat in the forest with three decepticons simultaneously (prior to his "death"). This was the best part of the film, and it captures the essence of just how awesome Optimus Prime really is. Here's a guy that can take on three decepticons at once (including Megatron), unload an impressive ass kicking, and yet yearn for nothing but peace. That's strength. That's honour. That's why he's my favourite.</li><li>Prime modified with Jetfire's parts. I think that it would have been cool if he had been able to retain those parts longer than the final battle and actually be able to transform into vehicle mode with them. I wonder what that would have looked like.</li><li>Watching the Constructicons merge into Devastator. That was a delicious segment to watch, though honestly I was kind of disappointed with the final merged form. I didn't really like the whole "vacuum mouth" thing or the ape-like lumbering. I also would have liked to see Devastator enter into some hand-to-hand combat with some Autobots.<br /></li></ol>Now for the stuff that I didn't like, in no particular order:<br /><br /><ul><li>Lousy female characters. Why is it that it seemed like every female at the college was portrayed as a sex-starved bimbo? That whole thing with Rainn Wilson dropping his apple at the feet of a female freshman? That whole creepy Alice thing? Both moments just seemed degrading. </li><li>Sam's mom high on weed? Give me a break. I was amused with Sam's parents in the first film - I thought of them as an exaggerated realization of how many of us probably perceive our parents at Sam's age. This time they should have been given the hook as soon as Sam left for college.</li><li>Mikaela: Why was she even there? She was contributing to the story when she took the Allspark shard, when she caught Wheelie, and when she disposed of Alice. For the latter two-thirds of the film, why was she even necessary (other than for gratuitous slow-motion shots of bouncing cleavage, that is)?<br /></li><li>The Twins, Mudflap and Skids. I agree with everybody who says that these guys are Michael Bay's equivalent to Jar-Jar Binks. You just know they're annoying when Bumblebee has to start knocking them around to get them to shut up. As soon as I found out that they were voiced by the voice of SpongeBob SquarePants, their idiocy suddenly made a bit more sense. To be fair, I did like some of their one-liners, but they could have easily gone to somebody cool, like Ironhide.</li><li>Lennox and Epps: These guys had shit lines. They had much better character development in the first movie (and that's not saying much). These guys were one-dimensional in this film. Furthermore, while executing the Egyption Prime Drop <span style="font-style: italic;">[Wow, that sounds like a good name for a drink! You heard it here first, you plagiarizing weasels!]</span>, they kept going on about how "the kid" had better not be wrong about all of this. Hello?! Weren't you two there in the first film when Sam fought alongside you (and arguably did a more important job) to save the world? Or does that whole "You're a soldier now" speech from the first film no longer apply now that Lennox is a Major? LAME.</li><li>Megatron subservient to The Fallen? Sorry, Megatron answers to nobody. That's why he's Megatron. The only time that he can be coherced is via torture. I guess that the writers never watched <a href="http://www.imdb.com/title/tt0092106/"><span style="font-style: italic;">Transformers: The Movie</span> (1986)</a>. I know that the live-action films are not 100% faithful to the animated series, but this servitude tore out an essential part of Megatron's character.</li><li>Optimus Prime's "death." I thought that this was executed poorly, especially vis-à-vis Megatron's death and resurrection. I also thought that he went down too easily considering that he was the only worthy opponent to The Fallen. I was also disappointed that this resulted in Prime's absence from much of the length of the film. The only redeeming part of this was the invocation of the Matrix of Leadership.</li><li>Cinematography. Some of the action sequences involved cameras that were placed so close to the action that all I could see was a blurry mess of colours. It was often times very difficult to follow. More than once I wondered whether this was just a way to stretch the effects budget a bit further by making the opponents indistinguishable.<br /></li><li>Length. I love <span style="font-style: italic;">Transformers</span>, but in my opinion this film would have been better off as a 100 to 110 minute film.</li></ul>Will I buy this film on Blu-ray? Probably - I'm a sucker for Optimus Prime. Was I disappointed? As much as it pains me to admit it, I think that I was.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-13334265635006818132009-06-17T21:49:00.004-06:002009-06-17T22:39:50.023-06:00Oldies but GoodiesToday I was going through some old versions of my personal home page when I found some links to a number of <span style="font-style: italic;font-family:times new roman;" >math</span><span style="font-weight: bold;font-family:arial;" >NEWS</span> articles that I was responsible for. I found them to still be humourous, so I thought that I'd share the links.<br /><br /><a href="http://www.mathnews.uwaterloo.ca/Issues/mn9001/cigar.php">Secondhand Stogies</a><br /><br /><a href="http://www.mathnews.uwaterloo.ca/Issues/mn9003/partypants.php">Party in Your Pants</a><br /><br /><a href="http://www.mathnews.uwaterloo.ca/Issues/mn9204/pinto.php">Roadside Attractions: Pinto McBean</a><br /><br /><a href="http://www.mathnews.uwaterloo.ca/Issues/mn9402/javasucks.php">War on Java</a><br /><br />and my personal favourite:<br /><br /><a href="http://www.mathnews.uwaterloo.ca/Issues/mn9201/charad.php">Speaking English Deemed Offensive to Minorities</a><div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0tag:blogger.com,1999:blog-6848237829518745137.post-80021615453518094292009-06-12T21:06:00.005-06:002009-06-14T21:21:50.307-06:00Game 7, 2009 Stanley Cup FinalI want to take a moment to complain about the CBC's coverage once the buzzer ended the final game of the 2009 NHL playoffs.<br /><br />Why was it that every time something important was going on, they were doing an interview? We all want the interviews, but I'd like to actually hear the announcement of the recipient of the Conn Smythe Trophy when it happens. I thought that they were going to miss everything when the cup came out because, when the rink announcer introduced the Stanley Cup, again they were interviewing. Finally, they were interviewing Malkin just as Lemieux was lifting the cup over his head - a significant moment indeed.<br /><br />I thought that this was all terrible TV. You need to get the interviews, but you need to recognize that there are significant events taking place that the audience wants to see and hear.<br /><br />Finally, you'd think that with all the Crosby coverage that he was the Conn Smythe winner. He's basically been crowned as Gretzky's successor. He's a good player, but when other players are winning the hardware, I think that they should be getting more than a sliver of coverage. Hell, that Pittsburgh-themed Gatorade commercial that they aired during the break might as well have been called <span style="font-style: italic;">The Sidney Crosby Show</span>.<br /><br /><span style="font-weight: bold;">EDIT:</span> Looks like Bruce Dowbiggin at <span style="font-style: italic;">The Globe and Mail</span> <a href="http://www.theglobeandmail.com/sports/sid-and-the-pens-storyline-is-missing-malkin/article1181712/">feels the same way</a>.<div class="blogger-post-footer"><br/><br/>This post was originally published on the <a href="http://ohmsblog.teamohms.org">OhmsBlog</a>.<br/>
Some of the original formatting might have been stripped from this feed. To comment on this post or read other blog entries, visit<br/>
<a href="http://ohmsblog.teamohms.org">http://ohmsblog.teamohms.org</a></div>Aaronhttp://www.blogger.com/profile/02157963923872966196noreply@blogger.com0