Revised editions of two significant programming books are due this May.
- The C++ Programming Language, 4th Edition
(updated for C++11)
- Advanced Programming in the UNIX Environment, 3rd Edition
Takes me back to 1993 — in a good way.
Revised editions of two significant programming books are due this May.
Posted by Jonathan Dodds on May 11, 2013 at 09:52 PM in C/C++, Mac OS X, Software Development | Permalink | Comments (0) | TrackBack (0)
When I'm working out a software design I depend a great deal on my instincts, my unconscious competence. My instincts are in part an internalization of my experience.
I don't wait for experience to happen; I proactively build my experience through practice and experimentation.
Always be learning.
Posted by Jonathan Dodds on November 07, 2010 at 08:57 PM in development, Programming, Software Development | Permalink | Comments (2) | TrackBack (0)
It’s over.— John Gruber
Daring Fireball Linked List: Microsoft Changes Strategy With SilverlightIf you’re targeting the web as a platform, if you’re looking to maximize the browsers that can be supported, if you’re looking to maximize the lifetime of your code base, don’t build to Adobe Flash and, now, don’t build to Microsoft Silverlight.
Posted by Jonathan Dodds on October 30, 2010 at 10:46 AM in Software Development, Web/Tech | Permalink | Comments (0) | TrackBack (0)
In The Keyboard Cult on his Coding Horror blog, Jeff Atwood surveys mechanical keyboards. But he introduces his survey thusly:
As a guy who spends most of his day typing words on a screen, it’s hard for me to take touch computing seriously.
He trots out Scott Adams trotting out the notion that iOS is for consumption and not creation.
And deeper into his post he writes:
Maybe I’m biased. As I recently remarked on programmers.stackexchange.com, I can’t take slow typists seriously as programmers. When was the last time you saw a hunt-and-peck pianist?
Wow.
Touch typing on a full size keyboard, thumb typing on a physical smart phone keyboard, and 'typing' on a touch keyboard are different skills. The first two skills are related. The last skill is entirely different. The fastest raw typing speed on a touch keyboard will probably never approach the fastest typing on a physical keyboard. The real question is not which is faster, but if typing on a touch keyboard can be fast enough.
Atwood has highly developed touch typing skills and he's understandably frustrated that those skills don't transfer but how much effort did he put into developing 'typing on touch' skills before dismissing touch computing?
Activities that would be characterized as passive consumption requires less complex interactivity than creation activities regardless of the platform. But the disparity between consumption and creation activities is more apparent on iOS. There is a learning curve. It's not a steep curve but it's there. And it's a disservice to pretend otherwise.
iOS is a very young platform. It's already well established but the platform and the applications on the platform have a lot of growing and maturing to look forward to. Some of the tasks that are awkward and slow today will not be in the future. That doesn't help today of course.
Still Atwood's dismissal of touch computing seems extreme. Maybe he's link baiting.
Which brings us to the 'hunt-and-peck pianist'. There are many skills that make for a good programmer. Fast typing is not one of them. Atwood's analogy is plain bad.
Computer Science is no more about computers than astronomy is about telescopes.— Edsger W. Dijkstra
This post was written on an iPad.
Posted by Jonathan Dodds on October 24, 2010 at 09:59 PM in iOS, Programming, Software Development | Permalink | Comments (1) | TrackBack (0)
Designing and writing multi-threaded code is hard. Threading adds a debilitating amount of complexity to the Von Neumann architecture and, despite 50+ years of theory and implementation, the threading support in most programming languages is low level, the equivalent of malloc rather than GC managed memory.
If you’re a developer and you approach threading with trepidation then here are some experience tested guidelines and techniques that I try to follow. The overall strategy is to reduce the surface area for bugs and to increase the maintainability of the code. These are not hard and fast rules. It’s important to be pragmatic and to adjust for the specific situation.
But first, please note that I’m no expert. I’m sharing what I’ve learned and I know I’m not done learning. If this post helps you, I want to know. If you think I’ve got something wrong, I want to know. Thank you.
Avoid sharing data.
Sharing data between threads is the most common source of threading bugs. By definition many of the nastiest threading bugs can only occur when data is shared. Safely sharing data requires synchronization either in the form of a lock or an atomic primitive. Getting the synchronization right is not always easy. Avoid sharing data and the threading bug potential goes way down. As a bonus, the code can potentially run faster because the possibility of synchronization contention is eliminated.
It may seem that without sharing data, threads are pretty useless. Not so. But this is a case where you need to be willing and able to trade space for speed and safety. Give a thread it’s own private copy of everything it may need, let the thread run to completion (‘wait’ or ‘join’ on the thread), and then collect the results of it’s computations.
Avoid sharing logging.
Logging is data. See above. Sometimes the messages generated on a thread need to be reported more or less immediately. But if a delay can be tolerated, a thread can have it’s own private log that gets collected after the thread has run to completion.
When you must share, prefer lighter weight synchronization mechanisms.
I didn’t understand lock-free threading until I understood that lock-free doesn’t mean no synchronization mechanisms at all.
Under Windows the Interlocked* APIs represent atomic operations implemented by specific processor instructions. Critical sections are implemented via the interlocked functions. The interlocked functions and the critical section on Windows and the equivalent on other platforms are generally the lightest weigh synchronization mechanisms.
Technically the interlocked functions are not locks, they are hardware implemented primitives. But colloquially developers will speak of ‘locks’ and mean the whole set of synchronization mechanisms, hence my confusion over lock-free threading.
Having said that I will now forgo rigor and refer to all synchronization mechanisms as ‘locks’ because it’s pithier.
Use the smallest number of locks possible.
Don’t treat locks like magic pixie dust and sprinkle them everywhere. Synchronization locks provide safety but at a cost in performance and a greater potential for bugs. Yes, it’s kind of paradoxical.
Hold locks for the shortest length of time possible.
A critical section, for an example, allows only one thread to enter a section of code at a time. The longer the execution time of the section, the longer the window for other threads to box car up waiting for entry.
If there are values that can be computed before entering a critical section, do so. Only the statements that absolutely must be protected should be within the section.
Sometimes a value needs to be retrieved from a synchronized source, used as part of a computation, and a product of the computation needs to be stored to a synchronized destination. If the whole operation does not need to be atomic then, despite the desire to minimize locks, two independent synchronization locks could be better than one. Why? Because the source and destination are decoupled and the combined lock hold time is reduced because only the source read and the destination write are covered.
Be DRY - Don’t Repeat Yourself.
Always a good policy, being DRY has special importance with threaded code. There should be only one expression or implementation of any given synchronized operation. Every thread should be executing the same code to ensure that the operation is performed consistently.
Design to ensure that every acquisition of a lock is balanced by a release of the lock.
Take advantage of the RAII pattern or the dispose pattern or whatever is appropriate to the language and platform. Don’t rely on the developer (even when the developer is yourself) to remember to explicitly add every release for every acquisition.
Finish the threads you start.
Don’t fire and forget. Wait or join and clean up your threads and their resources.
Don’t let threads you created outlive the primary thread in the process. Some platforms have the unfortunate design of performing runtime set up and initialization, calling the the program code, and then tearing down and de-initializing on the primary thread. Other threads that may still be running after the primary thread exits may fail when the runtime is pulled out from underneath them.
Don’t kill a thread. That will leave data in an undeterminable state. If appropriate implement a way to signal your thread to finish.
Don’t get focused on locking the data when it’s the operation that needs to be synchronized.
It’s easy to get fixated on the shared data but often times the design is better served by paying more attention to the shared operations.
Avoid nested locks.
Acquiring lock A and then lock B in one part of the code and elsewhere acquiring lock B and then lock A is asking for a deadlock. No-one sets out to write a deadlock. But sometimes the code isn’t exactly what was intended or a change is made and the impact of the change isn’t fully understood. If locks are never nested then the potential for this kind of deadlock just doesn’t exist.
If locks must be nested then alway acquire the locks in the same order and always release the locks in the same opposite order. And be DRY about it.
Avoid method calls within a lock.
This may seem aggressively limiting but method implementations can change and introduce new issues like unwanted nested locks. Keeping method calls out of the scope of locks reduces coupling and the potential for deleterious side effects.
(I think of this one as following in the spirit of LoD.)
Encapsulate intermediate values.
Avoid creating inconsistent state in shared data. Operations that create intermediate values shouldn’t be performed in situ on shared objects or data structures. Use local temporaries for intermediate values. Only update the shared data with the end products of a operation.
Be careful of using the double check lock pattern.
It’s a wonderful idea that’s so clever that’s it’s broken in many languages and runtime platforms.
Where the DCLP is not just inherently broken and can be used, it still needs to be implemented correctly. Mark the variable under test as ‘volatile’ (or the closest equivalent) so that the language compiler doesn’t optimize away one of the two checks. Don’t operate directly on the variable under test. Use a temporary to hold intermediate values and wait to assign to the variable under test until the new state is fully computed.
The DCLP is often used for one time or rare initializations.
Don’t lock the ‘this’ pointer.
If there are member functions of an object that need to synchronize, create a private member variable that is used as the lock target rather than locking on ‘this’. In addition to encapsulating the locking, using a private member guards against potential issues where a framework, library, or runtime may be performing synchronization by using the ‘this’ pointer.
Explain your code.
Leave comments for yourself and the next person.
Strive to make the design grokkable.
The original design and implementation can be bug free but if the design is hard to understand and follow, bugs can be introduced in maintenance cycles. Even if you are the only developer who may ever touch the code, remember that the details of the design that are so crystal clear in your mind today will get supplanted by tomorrow’s events and miscellany like … Squirrel!
Posted by Jonathan Dodds on August 22, 2010 at 10:12 PM in .Net, C#, C/C++, Java, Programming, Software Development | Permalink | Comments (0) | TrackBack (0)
Software cries out for personality, for ornamentation, for delight. To reflect higher aspirations, and evoke emotion.
— Amy Hoy
Cheerful: Don’t listen to Le Corbusier—or Jakob Nielsen
Posted by Jonathan Dodds on April 27, 2010 at 10:01 PM in Software Development, Web/Tech | Permalink | Comments (0) | TrackBack (0)
The System.Diagnostics.Stopwatch class in the .Net runtime can be used to time operations. Bit there’s a problem in .Net 3.5 and earlier. The Elapsed* properties (Elapsed, ElapsedMilliseconds, ElapsedTicks) will sometimes return a negative value.
There’s a note in the documentation:
On a multiprocessor computer, it does not matter which processor the thread runs on. However, because of bugs in the BIOS or the Hardware Abstraction Layer (HAL), you can get different timing results on different processors. To specify processor affinity for a thread, use the ProcessThread.ProcessorAffinity method.
Some advocate setting processor affinity to eliminate the possibility of a negative elapsed time. But a common work-around is to simply use zero whenever the elapsed time is negative.
In recent work I used the Stopwatch class to time operations performed by Threads in a ThreadPool and I was getting false timings because of the negative elapsed time issue. I was leery of messing with the threads’ processor affinities because the ThreadPool is a shared resource. But I had concerns about the ‘convert to zero’ work-around.
Through a Microsoft contact I was able to confirm that the work-around of checking for ‘elapsed < 0’ is adequate. The negative time issue occurs only when measuring very small time periods. The issue is fixed in .Net 4.0 and the fix in the Stopwatch is to check that if elapsed < 0 then set elapsed to zero.
Posted by Jonathan Dodds on January 17, 2010 at 10:21 AM in .Net, Microsoft, Programming, Software Development | Permalink | Comments (0) | TrackBack (0)
The key to success is reducing the cost of failure.— Joi Ito, as reported on Twitter by Leo Laporte
Posted by Jonathan Dodds on December 15, 2009 at 10:56 PM in Software Development | Permalink | Comments (0) | TrackBack (0)
Michael Hauck's process for custom designs is applicable to software.
(Via Daniel Jalkut.)
Posted by Jonathan Dodds on December 15, 2009 at 09:56 PM in Software Development | Permalink | Comments (0) | TrackBack (0)
Apple introduced the Macintosh in 1984 and in 2007 Apple introduced the iPhone. The case has been made that with the Macintosh, Apple squandered its technological lead by maximizing profits when it should have been maximizing market share. The $99 iPhone is why 2007 won’t be like 1984.
According to TUAW, Boingo Wireless reported that for January through May of 2009 “89.2% of all mobile devices accessing Boingo’s airport hotspots” were iPhones.
TUAW: iPhone dominates Boingo airport access study
John Gruber writes about “the heart of the revolution at hand”, that mobile devices like the iPhone will supplant portable devices like notebooks.
Daring Fireball: Meg Hourihan on the iPhone as a Computer
If I speculate that ‘mobile computing’ will become more ubiquitous (not a stretch) and that the iPhone platform is already dominating in that space (open to interpretation) and if I’m developing a web site that needs to reach the mobile computing based audience, then I need to support Mobile Safari. If mobile devices become the ‘hottest’ part of the market for computing hardware and the iPhone platform becomes unequivocally dominant then Safari will be the new Internet Explorer. (Lazy or incompetent developers There will be developers who through laziness or incompetence of their own or of others will write only to Safari — but I think, given that it’s a hardware company, Apple can be a better steward of the public interest in a robust and interoperable web.)
Mad speculation? Maybe. But Flash doesn’t make the short list of technologies I plan to continue investing time and resources in learning.
Posted by Jonathan Dodds on July 02, 2009 at 10:16 PM in Apple, Programming, Software Development, Web/Tech | Permalink | Comments (0) | TrackBack (0)