Я потратил сегодня почти весь день пытаясь найти и поправить очень странную ошибку в одном из наших серверных приложений, написанных на Perl. И, как выяснилось в процессе, в системных библиотеках Perl или, может быть, в его интерпретаторе, есть крайне неприятная ошибка.

Проблема в следующем. Если вы попытаетесь использовать модуль “threads” вместе с модулем “Thread::Semaphore”, как это описано в официальной документации по языку Perl (perlthrtut), вы получите утечку памяти размером около 4kb на каждый вызов $semaphore->up. Следовательно, следующий простой пример кода вызовет просто огромные утечки памяти (около 100 Mбайт в секунду на моем тестовом сервере):

#!/usr/bin/perl

use threads;
use Thread::Semaphore;

my $xxx = new Thread::Semaphore();

my $x = new threads(\&mythread);
$x->join;

sub mythread {
    while (1) {
        $xxx->down();
        $xxx->up();
    }
}

После 5 часов секса веселья с нашим проектом и создания приведенного выше тесткейса я решил посетить “официальныйl” IRC-канал Perl’а (#perl) на irc.perl.org. Народ там оказался, мягко говоря, грубоват и помочь не захотел… Спасибо тому единственному участнику чата, который честно послал меня в сторону канала #p5p, где обитает народ, занимающийся багами (я так понял). Этот канал был практически мертв (хотя, может быть дело было в том, что в америке была ночь), но я нашел там одного отличного парня. Его имя Sam Vilain. Он потратил уйму времени и выяснил, что вызов Perl’ового bless на shared-переменных приводит к необъяснимому memory leak’у (у меня - 4Кб на вызов).

Благодаря подсказкам и советам Sam’а я написал простой модуль для Perl’а, который может быть прозрачно использован как замена стандартному Thread::Semaphore. То есть просто делаете use Sema4; и все становится хорошо:

package Sema4;

use threads::shared;

sub new {
    my $class = shift;
    my $val : shared = @_ ? shift : 1;

    # Workaround because of memory leak
    return bless \\$val, $class;
}

sub down {
    my $s = shift;
    # Double dereferencing
    $s = $$s;
    lock($$s);
    my $inc = @_ ? shift : 1;
    cond_wait $$s until $$s >= $inc;
    $$s -= $inc;
}

sub up {
    my $s = shift;
    # Double dereferencing
    $s = $$s;
    lock($$s);
    my $inc = @_ ? shift : 1;
    ($$s += $inc) > 0 and cond_broadcast $$s;
}

1;

Собственно, выводом из всего произошедшего стала мысль… А не стоит ли нам выбросить на помойку старые “мертвые” языки, комьюнити которых не способно реализовать стандартные треды в 21-м веке? А, самое главное, комьюнити которых тупо плюет в лицо человеку пришедшему с проблемой в их core libraries? Я думаю, что мне и правда стоит попробовать что-нибудь другое… Вот только не знаю, что именно… может быть Ruby (ну не нравится мне синтаксис Python’а)? Посмотрим…