16 февр. 2013 г.

Gentoo: Сборка GCC 3.2 в современном окружении

В Gentoo есть ebuild-ы для GCC старых версий, однако GCC 3.2 не собирается последними версиями GCC, в том числе текущим стабильным 4.6. Возможное решение следующее: с помощью GCC 4.6 собрать GCC 3.4, а с помощью GCC 3.4 уже собрать GCC 3.2.

Шаг 1: версия bison


GCC 3.2 не собирается текущим стабильным bison 2.x. Для bison слотов нет, так что для сборки GCC можно просто замаскировать новые версии. У меня работает bison-1.875d.

/etc/portage/package.mask

>=sys-devel/bison-2.0

# emerge -u sys-devel/bison

Шаг 2: настройка окружения


См. /etc/portage/env.
  1. Во-первых, GCC версии 3.x могут не поддерживать ваши CFLAGS (в частности -march=native), поэтому отключим их для этих пакетов.
  2. Во-вторых, мы должны указать, что для сборки GCC 3.2 будет использоваться GCC 3.4.
/etc/portage/package.env

# Отключаем CFLAGS для GCC 3.x
=sys-devel/gcc-3* use-simple-cflags.conf

# Включаем использование GCC 3.4 для GCC 3.2
=sys-devel/gcc-3.2* use-gcc-3.4.conf

 /etc/portage/env/use-simple-cflags.conf 

CFLAGS="-O2"
CXXFLAGS="-O2"

/etc/portage/env/use-gcc-3.4.conf 

CC="gcc-3.4.6"


Шаг 3: сборка GCC 3.4


# emerge sys-devel/gcc:3.4


Шаг 4: сборка GCC 3.2


# emerge sys-devel/gcc:3.2
# /usr/i686-pc-linux-gnu/gcc-bin/3.2/gcc --version

13 февр. 2013 г.

Emacs, tramp: File ... is read-only on disk. Change buffer mode?


Это сообщение может появиться, если соединение разорвалось и tramp не может установить повторное соединение самостоятельно.

Решение:

M-x tramp-cleanup-all-connections

Затем можно обновить буфер в dired или повторно сохранить файл.

2 февр. 2013 г.

Emacs: тормоза, плавная прокрутка


Ссылки

  1. Возможные причины медленной работы
  2. Плавная прокрутка

Возможные симптомы и их причины


1. Задержки при наборе текста


Это возможно при использовании модуля tabbar, старый баг. AFAIK, его мало кто использует.

2. Задержки при прокрутке и построчном перемещении курсора

Возможные причины:
  1. Самое главное, из-за linum-mode (нумерация строк).
  2. Из-за font-lock-mode (применение цветов и шрифтов к тексту), когда в буфере слишком много разноцветных жирных шрифтов.
  3. Из-за hl-line-mode (подсветка текущей строки).

Плавная прокрутка без тормозов и без отключения перечисленных режимов возможна, но, как и положено, опциональна и отключена по-дефолту.

Настройки прокрутки

;; При прокрутке применять font-lock не сразу, а после небольшой задежки.
(setq jit-lock-defer-time 0.01)

;; Эту опцию часто советуют выставлять в t, но я не заметил разницы с nil.
;(setq redisplay-dont-pause t)

;; Опционально: медленная плавная прокрутка колесиком.
(setq mouse-wheel-scroll-amount '(2 ((shift) . 2)))   ; Прокручивать по 2 строки.
(setq mouse-wheel-progressive-speed nil)
(setq mouse-wheel-follow-mouse 't)

;; Опционально: никогда не прокручивать более, чем на 1 строку при перемещении курсора за
;; нижнюю или верхнюю границу экрана.
(setq scroll-conservatively 10000)

;; Опционально: отступ от верха и низа экрана в 1 строку, при попадании курсора за отступ
;; происходит прокрутка.
(setq scroll-margin 1)

См. также Bug #12936.

Настройки нумерации строк

Дело в том, что linum-mode перерисовывает номера строк во время прокрутки, из-за чего она и тормозит. Мне известны 2 альтенативы:

  1. nlinum-mode: успешно решает в точности описанную проблему. Доступен на github.
  2. setnu-mode: предшественник linum-mode, работает почти без задержек, но есть баги, в том числе при использовании совместно с модулем auto-complete.
Стандартный linum позволяет установить hook на генерацию формата номеров строк и face для них, причем они могут быть свои для каждой строки. В nlinum это на данный момент не поддерживается, но такую возможность легко добавить, см. nlinum--region.

Справедливости ради замечу, что на 8-ядерном компьютере прокрутка у меня все же немного плавнее, чем на 2-х ядерном.

23 дек. 2012 г.

C++11: std::enable_shared_from_this, исключение std::bad_weak_ptr


Ссылки


Если при вызове shared_from_this() генерируется исключение  std::bad_weak_ptr, это означает, что еще не существует ни одного std::shared_ptr, ссылающегося на объект. См. Notes.

Неправильно:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <memory>

struct Class : std::enable_shared_from_this<Class>
{};

int main()
{
    Class *c = new Class;
    
    c->shared_from_this();
    
    return 0;
}

terminate called after throwing an instance of 'std::bad_weak_ptr'
  what():  std::bad_weak_ptr

Правильно:

#include <memory>

struct Class : std::enable_shared_from_this<Class>
{};

int main()
{
    Class *c = new Class;
    
    std::shared_ptr<Class> sc(c);
    
    c->shared_from_this();
    
    return 0;
}

16 дек. 2012 г.

Unix domain sockets: гарантированное освобождение и повторное использование сокета

Ссылки

Для связи процессов на одном компьютере можно использовать доменные сокеты (IPC-сокеты).

Отличия от интернет-сокетов:
  • Более высокая производительность.
  • Доставка SIGPIPE сразу после обрыва соединения с другого конца.
  • Стандартные права доступа unix у файлов сокетов.
См. также здесь.

Производительность можно измерить с помощью netperf:

$ netperf -t TCP_STREAM      # TCP
$ netperf -t STREAM_STREAM   # unix domain (компилировать netperf с опцией --enable-unixdomain)

Проблема

Файл сокета создается при вызове bind(2). Если файл уже существует, возвращается ошибка EADDRINUSE.

Единственный способ повторного использования файла сокета - вызов unlink(2) перед bind.

Есть две схемы:
  • Вызывать unlink перед завершением сервера. Тогда нет гарантии, что при крахе сервера не останется висячего файла, который пользователю придется удалять вручную.
  • Вызывать unlink при запуске сервера. Тогда нет гарантии, что не будет удален файл другого запущенного экземпляра сервера.

Решение

Именованные доменные сокеты бывают двух типов: filesystem sockets и abstract namespace sockets, см. unix(7).

Filesystem sockets

Простое решение - использовать дополнительный lock-файл для каждого сокета.

Этот файл может быть заблокирован тогда и только тогда, когда в данный момент запущен другой экземпляр сервера. Если блокировку удалось захватить, можно безопасно вызывать unlink() для файла сокета.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const char *socket_path = "/tmp/server-socket";

int server = socket(PF_LOCAL, SOCK_STREAM, 0);
assert(server != -1);

struct sockaddr_un server_addr;
bzero(&server_addr, sizeof(server_addr));

server_addr.sun_family = AF_LOCAL;
strcpy(server_addr.sun_path, socket_path);

char lock_path[256];
sprintf(lock_path, "%s.lock", socket_path);

//
// Open lock file
//
int lock_fd = open(lock_path, O_RDONLY | O_CREAT, 0600);
if (lock_fd == -1) {
    printf("can't open lock file\n");
    exit(1);
}

//
// Acquire lock
//
int ret = flock(lock_fd, LOCK_EX | LOCK_NB);
if (ret != 0) {
    printf("address already in use!\n");
    exit(1);
}

//
// Remove socket file
//
unlink(socket_path);

//
// Create socket file
//
ret = bind(server, (struct sockaddr *)&server_addr, sizeof(server_addr));
assert(ret == 0);

Abstract namespace sockets

Еще более простое решение - использовать abstract namespace (поддерживается только в Linux).

Если в  структуре sockaddr_un в поле sun_path в первый байт имени сокета записать '\0', сокет с этим именем будет создан не в файловой системе, а абстрактном пространстве имен.

Такой сокет будет автоматически освобожден при завершении приложения.

9 дек. 2012 г.

Tcl/Tk: встраивание Tcl-консоли в приложение Tk

Ссылки

Есть программа tkcon, которая реализует продвинутую консоль Tcl, с историей, автодополнением и подсветкой синтаксиса. Ее автор, Jeffrey Hobbs (соавтор книги Practical Programming in Tcl/Tk), поддерживает также пакет megawidget, в который включен виджет Console, основанный на tkcon.

Пакет разрабатывается вместе с tcllib, но распространяется отдельно. Скачать последнюю версию можно здесь (на данный момент это widget-20100219). Этот виджет можно использовать, чтобы предоставить пользователю возможность выполнять Tcl-команды в контексте интерпретатора своего приложения.

Пример



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#! /usr/bin/wish

lappend auto_path /path/to/megawidget/library

package require Console

entry .myentry -highlightcolor red -highlightthickness 2

console .myconsole -setgrid 0 -showmenu false

pack .myentry .myconsole -expand true -fill both

Примечания


Использование в Tcl 8.6

В Tcl 8.6 на данный момент (стабильного релиза еще не было) сломана обратная совместимость в обработке виртуальных событий. Из-за этого в Console не работают некоторые биндинги, в том числе клавиши "вверх" и "вниз" для навигации по истории.

См. Bug #3582795. Автор виджета в курсе событий и написал мне, что пока что не ясна причина поломки совместимости в Tcl, поэтому исправления еще не внесены. Есть также workaround, который исправляет работу клавиш "вверх" и "вниз".


diff -rup widget-20050712.orig/library/console.tcl widget-20050712/library/console.tcl
--- widget-20050712.orig/library/console.tcl 2005-07-13 10:05:18.000000000 +0400
+++ widget-20050712/library/console.tcl 2012-11-30 22:57:16.590578106 +0400
@@ -1597,20 +1597,24 @@ bind Console <<Console_KillLine>> {
     }
 }
 bind Console <<Console_Clear>> [namespace code { _clear [winfo parent %W] }]
-bind Console <<Console_Prev>> [namespace code {
-    if {[%W compare {insert linestart} != {promptEnd linestart}]} {
- tkTextSetCursor %W [tkTextUpDownLine %W -1]
-    } else {
- _event [winfo parent %W] -1
-    }
-}]
-bind Console <<Console_Next>> [namespace code {
-    if {[%W compare {insert linestart} != {end-1c linestart}]} {
- tkTextSetCursor %W [tkTextUpDownLine %W 1]
-    } else {
- _event [winfo parent %W] 1
-    }
-}]
+foreach key [event info <<Console_Prev>>] {
+ bind Console $key [namespace code {
+     if {[%W compare {insert linestart} != {promptEnd linestart}]} {
+   tkTextSetCursor %W [tkTextUpDownLine %W -1]
+     } else {
+  _event [winfo parent %W] -1
+     }
+ }]
+}
+foreach key [event info <<Console_Next>>] {
+ bind Console $key [namespace code {
+     if {[%W compare {insert linestart} != {end-1c linestart}]} {
+  tkTextSetCursor %W [tkTextUpDownLine %W 1]
+     } else {
+  _event [winfo parent %W] 1
+     }
+ }]
+}
 bind Console <<Console_NextImmediate>> [namespace code {
     _event [winfo parent %W] 1
 }]

Опция setgrid

При использовании Console с опцией setgrid (которая включена по-умолчанию) внутри panedwindow можно наткруться на Bug #3466588, из-за чего, в частности, нельзя развернуть окно полностью.

Чтобы избежать этого эффекта, достаточно создать консоль с опцией -setgrid false.

7 дек. 2012 г.

Tcl/Tk: встраивание окна внешнего приложения

Ссылки

В Tk есть несколько способов встроить окно внешнего приложения в виджет.

Способ #1


Использовать frame с опцией -container.
Приложению нужно передавать window-id контейнера.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#! /usr/bin/wish

wm geometry . 400x400+100+100

frame .container -container yes

frame .other_frame -bg blue

pack .container .other_frame -fill both -expand 1

set container_window_id [scan [winfo id .container] %x]

exec ./embedded_window $container_window_id &  

Способ #2


Использовать blt::container.
Приложению нужно передавать window-id контейнера.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#! /usr/bin/wish

package require BLT

wm geometry . 400x500+100+100

blt::container .container

frame .other_frame -bg green

pack .container .other_frame -expand 1 -fill both

set container_window_id [scan [winfo id .container] %x]

exec ./embedded_window $container_window_id & 

.container configure -name embedded_window


Способ #3


Использовать blt::container совместно с  TkXext.
В этом случае можно встраивать даже те приложения, которым нет возможности передать window-id.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#! /usr/bin/wish

lappend auto_path /path/to/TkXext

package require TkXext
package require BLT

wm geometry . 400x500+100+100

blt::container .container

frame .other_frame -bg red

pack .container .other_frame -expand 1 -fill both

exec ./embedded_window &

set child_window_id [TkXext.find.window embedded_window]

after 1000 {
    TkXext.reparent.window $child_window_id [winfo id .container]
    .container configure -window 0x$child_window_id
}


Тестовое приложение


Ниже приведен код программы, с помощью которой можно проверить, корректно ли передаются события изменения размера окна и нажатия клавиши при встраивании.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/*
 * Compile: gcc embedded_window.c -o embedded_window -lX11
 * 
 */
#include <X11/Xlib.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    Display *display = XOpenDisplay(NULL);
 
    int screen = DefaultScreen(display);
    
    Window parent_window = RootWindow(display, screen);

    if (argc == 2)
        parent_window = atol(argv[1]);

    Window child_window = XCreateSimpleWindow(display, parent_window,
                                              0,
                                              0,
                                              100,
                                              40,
                                              1,
                                              BlackPixel(display, screen),
                                              WhitePixel(display, screen));
    
    XSelectInput(display, child_window, KeyPressMask | ExposureMask);
    XMapWindow(display, child_window);
    
    XStoreName(display, child_window, "embedded_window");
    
    for (;;)
    {
        XEvent event;
        XNextEvent(display, &event);

        if (event.type == KeyPress)
        {
            printf("KeyPress\n");
        }
        
        if (event.type == Expose)
        {
            XWindowAttributes attrs;
            XGetWindowAttributes(display, child_window, &attrs);
                
            XClearWindow(display, child_window);
            
            XDrawLine(display, child_window, DefaultGC(display, screen), 0, 0, attrs.width, attrs.height);
            XDrawLine(display, child_window, DefaultGC(display, screen), 0, attrs.height, attrs.width, 0);
        }
    }
 
    XCloseDisplay(display);
    
    return 0;
 }