newtype Unsafe t = Unsafe t
getHTMLFormValue :: HTMLForm -> String -> String
getHTMLFormValue :: HTMLForm -> String -> Unsafe String
getHTMLFormValue = Unsafe (oldGetHTMLFormValue)
let (Unsafe unwrapped) = wrapped in ...
newtype UnsafeString = Unsafe String
unsafenessof a value and lose the fact that you did so.
foo (Unsafe x) = ...
escapeData :: Unsafe t -> t
unsafeExtractRawData :: Unsafe t -> t
unsafeExtractRawData (Unsafe x) = x
foo x = doSomeStuffWith (unsafeExtractRawData x)
instance (Eq t) => Eq (Unsafe t) where
(==) (Unsafe v1) (Unsafe v2) = v1 == v2
(/=) (Unsafe v1) (Unsafe v2) = v1 /= v2
unsafevalues, of course). However, for probably the most important Unsafe values -- Unsafe Strings -- I can't see how to do this. The problem is that String is not a typeclass, it's a synonym for [Char], a list of characters. The important string operations are either defined directly on the String type, or generically on [t]; i.e., "a list of anything". Because there is no typeclass, you simply can't overload these operations to apply to unsafe strings as well as regular strings.
liftUnsafe :: (t -> t) -> Unsafe t -> Unsafe t
liftUnsafe f (Unsafe v) = Unsafe (f v)
(++u) :: Unsafe [t] -> Unsafe [t] -> Unsafe [t]
(++u) = liftUnsafe (++)
It is funny to see how things have evolved since then. Being 6 at that time,
and PCs not being very common those days at home, I started using a computer at an age of
8 or so. It was an Amstrad PCW 8512,
a TV box sized computer which integrated a green screen monitor, two
"3" floppy bays and
a 9-pin dot matrix printer. It was powered by a 8bit Z80 processor, running at 4MHz and
had 512KB of RAM and no ROM at all. Main software was
LocoScript, a powerful word processor. It could be controlled by drop down menus
at a top toolbar, and initial screen was a file manager, in which you could manage
those text files in the floppy you booted from or a floppy in the other bay.
It had even support for some kind of multiuser support, as you could declare
in each floppy up to 7 user areas, with area 0 being a common root. Also,
it had support for limbo a place for deleted files.
Although LocoScript didn't need a separate OS to work, CPW 8512 could also
run CP/M OS,
which added programming capabilities (Maillard BASIC) and support for
spreadsheets and databases, along with some games as
Batman.
After using this system for a long time, my first PC was a 486, running
the new Windows 3.1 and later 3.11 for Workgroups version. I even installed
there a Windows 95 when it was released, and managed to create a partition
in the only 170MB HDD it had to install a test copy of IBM's
OS/2 Warp that came in a
CD bundled with PC World magazine.
Some time later my parents bought a Pentium 166MMX running Windows 95. I was 18 by then,
it was 1997, Internet was starting to be widely spread and used, and after a
couple of months at University I was installing my first Linux copy, which
was a RedHat based version. During next three years I installed different
distributions, as they were released with newer software and you could get them
in a CD with a magazine. Installing Debian Slink and having an Internet connection
available at my Student's Residence at the same time made me be able to
participate in mailing lists, send bug reports and download upgrades. I discovered
a new dimension in using Linux: collaboration.
[T]echnology across software domains should be consistent. There should be a standard, however de facto, that works everywhere. Skills should be portable across those markets, so that someone with experience in desktop software development has a decent chance of developing for handhelds. Everything should just work together, and development across all devices should be relatively straightforward to someone with experience in any one of them. Microsoft has been very good at this. […] I wonder whether the open source world can match that consistency. Granted, there are movements to make things consistent, such as the LAMP set of technologies for Linux, or the Linux Standard Base project. However, the structure of development for open source products militates against the establishment of any consistent standard. Open source development relies mostly on voluntary contributions. People don’t tend to contribute to ALL open source products (that would be impossible). Rather, they concentrate mostly on the products that interest them. This results in a developer version of tunnel vision. Since all that matters is the creation of the handful of products to which the volunteer contributes, what matters is the interests of contributors to a single project, not the interests of technology which should span ALL products.Great points, though I clearly disagree that the open source community is fundamentally unable to match Microsoft on providing a consistent environment. It’s going to take some work on our part though. One of the reasons Microsoft has historically been so successful is its unique ability to integrate disparate technologies into cohesive wholes, as well as to leave the mechanisms they use to do this just open enough that others can integrate with them to a degree but never so open that they can do so better than Microsoft can. Of course, Microsoft can do this because they develop, own, and control each of the pieces and the interfaces between them. Other platforms (Linux, Java, UNIX, even the Web to a certain extent) are comparative mishmashes for the exact reasons John describes, namely that every project is developed independently, and that there’s comparatively little thought put into how the pieces interact. The only parties that do think about these issues, the Linux distributors, tend to do so after the fact, and, of course, they have no direct control over the projects that produce the pieces to push any integration improvements they might make upstream (in the general case anyway). Furthermore, once multiple solutions in a given problem domain emerge, it’s not always possible to generalize the interfaces when the projects behind them don’t agree with each other on the “right solution”. In other words, because it’s an after-the-fact thing, “not invented here” and general inertia come into play. So, while the technical aspect of the problem is important, namely the interfaces between components (APIs at the source level and ABIs at the binary level), there’s a social aspect here too. We need to encourage the kind of competition at the implementation level that results in technical excellence while fostering a sense of cooperation at the interface level to ensure that, collectively, the various implementations fit seamlessly into a larger, consistent environment. In other words, we don’t want to get in the way of open source project and Linux distro innovation, but we do want to encourage open source projects and Linux distros to innovate in a collaborative fashion so the technology that ultimately wins in the marketplace can be standardized with as few legacy issues as possible (or, in the case where multiple best practices emerge, for legacy reasons or otherwise, we want to collaboratively define a uniform interface, i.e., cooperate on interface, compete on implementation, etc.). That’s the only way we’re going to get there.
make oldconfig from 2.4 I finally tried to boot with it. The most deep changes I did was:
Home-brewed Halloween fun: I was traumatized that I forgot my cross at home. I also apologize for any trauma caused by this picture. (Yes, all three of us are indeed girls.)
Edit: added in proper image thumbnail to not be evil.
<neli> why does it need so much memory ?After poking around at getting britney to run locally, and doing some basic analysis on how it was used, neli decided to rewrite the memory manager to use chunk-size pools – so that a separate pool would be used for 4 byte allocations, for 8 byte allocations, for 12 byte allocations, etc. That’s possible due to two factors: one is that the special memory allocation is only needed for data structures which are all made from fairly small sizes, and are a multiple of the word size. It also means that fragmentation can be avoided completely – since you can use the “free” elements to store a free list taking up no extra space. Happily, that change also made the memory allocation much simpler. (It also shows the benefits of a fresh look at code; I’d been thinking of doing something similar for ages, but always been stopped because I couldn’t figure how to make it cope with the various different sized blocks I’d need. As it turned out though, I’d solved that problem ages ago when I wrote a separate allocator to deal with strings, which would’ve required my malloc to cope with a much larger variety of sizes) What it didn’t do was actually solve the memory leak. Which was pretty confusing, since the old memory checker had some tests to ensure that memory wasn’t really being leaked, and if the memory’s not being lost due to a missing free, or being lost to fragmentation, where is it going? At this point, the real benefit of having a simpler malloc implementation came across; because suddenly it was plausible to add some debugging code to allocate a separate pool for every memory allocation, and to report how much memory is being used at any point, per line of code. That gave me output like:
<aba> neli: because it needs to check how the changes affect the installability count
<neli> aba: ok…but still does not seem like a big deal
<neli> how large trees are we talking then?
<aj> it has to do a lot of backtracking
<aj> how large is the longest dependency tree in debian?
<neli> backtracking only takes time, not space, right?
<aj> you need a list of things to backtrack over
<aba> neli: it also consumes memory, at least with the current dpkg-implem ntation
<aba> (well, there are ideas how to avoid this memory fragmentation, but that’s something else)
<aj> (it’s not actually memory usage, it’s memory fragmentation)
<neli> hmm, bad memory manager ?
<aj> imperfect, *shrug*
pool 8B/1:751; 1962683 used 1965960 allocated (99.8% of 22 MiB)So I knew that line 751 (of dpkg.c), was allocating 8B chunks, had used up to 22MiB in the past, and was currently using 99.8% of that. (Actually, I made an off by one error initially, so the “8B” above is actually 12B, as you can see if you divide 22MiB by 1965960) That let me see two “leaks”. One turned out to be fairly easy to diagnose; as an optimisation, when I say “package foo is installable”, I note that “but we used bar to see that, so if we later update/remove bar, note that we’ll have to see if foo is still installable”. So the datastructure for bar ends up with a linked list of all the packages, foo, that relied on it. But I don’t check that list for dupes, so if I note that both bar and baz were required for foo to be installable, then update bar, when I recheck foo, I’ll add it to both bar’s and baz’s lists; which is fine for bar, whose list I cleared when I updated it; but not so fine for baz, because it’s now got two entries for foo. Having already added a check for dupes when trying to see if my guess was right, extending that check to skip the addition was pretty easy. The other leak required a little more investigation. It was in a fairly tight loop which could reasonably be using a fair amount of memory; but by the end of the loop it should all be freed. Adding some more debug code to the allocation functions confirmed the problem:
<aj> pool 8B/11:1231; 1618157 used 1703936 allocated (95.0% of 13 MiB, 9.05% current)The code was meant to either add the allocated block to a larger list that’s being traversed, or to free it immediately – the problem turned out to be that there were some nested ifs, and an internal if was lacking an else that should have freed the block. Fixing that changed the results to:
<aj> so it looks like ~9% of those allocations are never freed to me
<aj> 9.05% = used/alloc
<aj> where used is incrememnted on b_m, decremented on b_f
<aj> alloc is incrememneted on b_m, never decremented
pool 8B/11:1228; 160 used 131072 allocated (0.1% of 1 MiB, 0.01% current)Which both dropped the leak – only allocating a maximum of 130,000 nodes instead of almost 2,000,000 – and resulted in almost all the allocations that had ever been made, having been freed once britney had been running for even a fairly short while. As it turned out, though, the cure for the first leak was worse than the disease – checking for duplicates in a linked list turned out to take too long, and the memory wasted in that section wasn’t that big a deal. Using a red-black tree or similar instead would’ve been a solution, but so far doesn’t seem necessary. There haven’t been any OOMs since, happily; and even better, the reduced bloat seems to have made the overall checking a bit faster. Sweet.
echo 'something\c' over echo -n something when both ‘\c’ and -n are understood by echo.
The second issue is that when giving some options with spaces to configure (like in --enable-optimize="-pipe -w -O2"), the associated variable gets backslashed spaces (i.e. "-pipe\ -w\ -O2") (thus, being an autoconf feature)
The third issue is that the mozilla configure.in script tries to get rid of these backslashed spaces with echo $enableval sed -e 's \\\ g', triggering the nasty dash bug. A simple workaround, as stated in #256384 is to quote $enableval
Now, for the dash bug itself.
What happens with dash is that whenever you echo a ‘\c’ escape sequence, all subsequent echo commands stop printing after the first argument containing a backslash. For example echo test1 'test2\ttest3' test4 outputs test1 test2<tab>test3.
In the case of our echo $enableval, since its value was -pipe\ -w\ -O2, it printed -pipe\ without any space after the backslash, so that it was not removed by the sed call. This value was then substitued in config/autoconf.mk.in to create config/autoconf.mk, where the ending backslash was interpreted as its shell meaning (continue on next line), thus breaking the variable, and build command lines.
And for the bad programming practice ?
The dash bug is due to a global variable that never got re-initialized, leaving the “terminate echo” (kinda) flag turned on (which is only checked when there is a backslash in the argument, since it is used to stop printing after, guess what… ‘\c’).
No wonder why one of the first things you learn in programming classes is : never use global variables. (or at least, be very very very careful about what you do with them)
Next.