디시인사이드 갤러리

갤러리 이슈박스, 최근방문 갤러리

갤러리 본문 영역

Self-pipe 기법을 이용한 Ada 시그널 핸들러 (코드 리뷰 부탁)

나르시갤로그로 이동합니다. 2025.07.24 09:15:29
조회 57 추천 0 댓글 2

제목: Self-pipe 기법을 이용한 Ada 시그널 핸들러 (코드 리뷰 부탁드립니다) 🚀

안녕하세요, Ada로 시스템 프로그래밍을 공부하고 있는 개발자입니다.

최근 유닉스(POSIX) 환경에서 시그널을 좀 더 안전하고 Ada스럽게 처리하는 라이브러리를 만들어보고 있습니다. 특히 시그널 핸들러의 비동기-안전(async-signal-safety) 문제를 해결하기 위해 self-pipe 기법을 적용해 보았는데, 다른 분들의 의견은 어떨지 궁금해서 코드를 공유하고 피드백을 요청합니다.

## 주요 설계

이 라이브러리의 핵심 설계는 다음과 같습니다.

  1. Self-Pipe 기법: 실제 시그널 핸들러에서는 write() 시스템 콜만으로 시그널 번호를 파이프에 쓰는 최소한의 작업만 수행합니다. 복잡한 로직은 모두 메인 이벤트 루프에서 파이프를 read()하는 dispatch 프로시저로 옮겨, 비동기-안전 제약 조건에서 벗어나도록 설계했습니다.
  2. 스레드-안전 핸들러 관리: 시그널 번호와 사용자 정의 핸들러를 매핑하는 자료구조를 protected object로 감싸, 멀티스레드 환경에서도 안전하게 핸들러를 등록하고 호출할 수 있도록 했습니다.
  3. 자동 자원 관리 (RAII): Ada.Finalization을 이용해 패키지 스코프가 종료될 때 생성된 파이프의 파일 디스크립터가 자동으로 close 되도록 구현하여 리소스 누수를 방지했습니다.

## 특징

  • 비동기-안전(Async-Signal-Safe) 시그널 처리
  • Ada의 강력한 타입을 활용한 안전한 API (Action 레코드, Number 타입 등)
  • 스레드-안전(Thread-Safe) 핸들러 등록 및 관리
  • 자동 자원 해제 (RAII)

## 고민되는 부분 및 질문

현재 dispatch 프로시저의 read 로직이 아직 미흡합니다. 지금 코드는 read의 반환 값이 0 이하이면 무조건 루프를 빠져나가는데, 이렇게 되면 논블로킹(non-blocking) I/O에서 정상적으로 발생하는 EAGAIN 같은 상황에 제대로 대처하지 못합니다.

bytes_read < 0일 때 errno를 확인해서 EAGAIN이나 EINTR 같은 경우를 구분하고, 실제 I/O 에러일 때는 예외를 던지는 식으로 개선해야 할 것 같은데, 이 부분에 대한 더 좋은 아이디어나 일반적인 처리 패턴이 있다면 조언 부탁드립니다!

## 전체 코드

-- clair-signal.adb
-- Copyright (c) 2025 Hodong Kim <hodong@nimfsoft.art>
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

with Interfaces.C;
with System;
with Clair.Error;
with Ada.Containers.Hashed_Maps;
with Ada.Finalization;
with Ada.Unchecked_Conversion;
with unistd_h;
with signal_h;
with sys_signal_h; -- For the C sigaction type
with errno_h;
with sys_types_h;
with Clair.Config;

package body Clair.Signal is
  use type Interfaces.C.Int;
  use type Interfaces.C.long;
  use type Interfaces.C.Unsigned;
  use type Interfaces.C.Unsigned_Long;
  use type Clair.File.Descriptor;

  -- Internal state for the self-pipe
  pipe_fds : aliased array (0 .. 1) of Clair.File.Descriptor := (-1, -1);

  -- pipe() 함수에 전달할 포인터 타입을 정의합니다.
  type C_Int_Access is access all Interfaces.C.int;

  -- System.Address를 C_Int_Access 타입으로 변환하는 함수를 인스턴스화합니다.
  function to_c_int_access is new Ada.Unchecked_Conversion
    (source => System.Address,
     target => C_Int_Access);

  -- The actual signal handler that will be r e g i s t e r e d with the kernel
  procedure clair_signal_handler
    (signo   : Interfaces.C.Int;
     info    : access sys_signal_h.siginfo_t;
     context : System.Address);
  pragma export (c, clair_signal_handler, "clair_signal_handler");

  procedure clair_signal_handler
    (signo   : Interfaces.C.Int;
     info    : access sys_signal_h.siginfo_t;
     context : System.Address)
  is
    signo_val     : aliased Interfaces.C.Int := signo;
    bytes_written : Interfaces.C.long;
  begin
    -- This is async-signal-safe
    bytes_written :=
      unistd_h.write
        (Interfaces.C.Int (pipe_fds (1)),
         signo_val'address,
         sys_types_h.Size_t (Interfaces.C.Int'size / 8));
  end clair_signal_handler;

  -- Implementation of the raise(3) wrapper
  procedure send (sig : Number) is
    result : constant Interfaces.C.Int := signal_h.c_raise (Interfaces.C.Int (sig));
  begin
    if result /= 0 then
      declare
        error_code : constant Interfaces.C.Int := Clair.Error.get_errno;
        error_msg  : constant String           :=
          "raise(3) failed: " &  "signal " & sig'image &
          " (errno: " & error_code'image & ")";
      begin
        case error_code is
          when errno_h.EINVAL => -- Invalid signal
            raise Clair.Error.Invalid_Argument with
                  "Invalid signal specified. " & error_msg;
          when errno_h.ESRCH => -- No such process
            raise Clair.Error.No_Such_Process with "Process not found. " &
                                                    error_msg;
          when errno_h.EPERM => -- Operation not permitted
            raise Clair.Error.Permission_Denied with "Permission denied. " &
                                                     error_msg;
          when others =>
            declare
              errno_text : constant String :=
                Clair.Error.get_error_message (error_code);
            begin
              raise Clair.Error.Unknown_Error with errno_text & ". " &
                                                   error_msg;
            end;
        end case;
      end;
    end if;
  end send;

  -- 수동 해시 함수 정의
  function hashfunc (key : Number) return Ada.Containers.Hash_Type is
  begin
    return Ada.Containers.Hash_Type (Interfaces.C.Int (key));
  end hashfunc;

  package Handler_Maps is new Ada.Containers.Hashed_Maps
    (key_type        => Number,
     element_type    => Handler_Access,
     hash            => hashfunc,
     equivalent_keys => "=");

  protected Handler_Registry is
    procedure r e g i s t e r (sig : in Number; handler : in Handler_Access);
    procedure call (sig : in Number);
  private
    handlers : Handler_Maps.Map;
  end Handler_Registry;

  protected body Handler_Registry is
    procedure r e g i s t e r (sig : in Number; handler : in Handler_Access) is
    begin
      if handler = null then
        handlers.delete (sig);
      else
        handlers.insert (sig, handler);
      end if;
    end r e g i s t e r;

    procedure call (sig : in Number) is
      -- 여기서 handler를 미리 선언할 필요가 없습니다.
    begin
      if handlers.contains (sig) then
        -- declare 블록을 사용해 지역 상수를 선언과 동시에 초기화합니다.
        declare
          handler : constant Handler_Access := handlers.element (sig);
        begin
          if handler /= null then
            handler.all (sig);
          end if;
        end;
      end if;
    end call;
  end Handler_Registry;

  -- Lifecycle management for automatic finalization
  type Finalizer is new Ada.Finalization.Limited_Controlled with null record;
  overriding
  procedure finalize (object : in out Finalizer);

  -- This object's declaration ensures finalize is called automatically
  finalizer_instance : Finalizer;

  overriding
  procedure finalize (object : in out Finalizer) is
    retval : Interfaces.C.Int;
  begin
    if pipe_fds (0) /= -1 then
      retval := unistd_h.close (Interfaces.C.Int (pipe_fds (0)));
    end if;
    if pipe_fds (1) /= -1 then
      retval := unistd_h.close (Interfaces.C.Int (pipe_fds (1)));
    end if;
    pragma unreferenced (retval);
  end finalize;

  function get_file_descriptor return Clair.File.Descriptor is
    (Clair.File.Descriptor (pipe_fds (0)));

  procedure set_action (sig : in Number; new_action : in Action) is
    sa     : aliased sys_signal_h.sigaction;
    retval : Interfaces.C.Int;
  begin
    retval := signal_h.sigemptyset (sa.sa_mask'access);

    if retval /= 0 then
      if retval = errno_h.EINVAL then
        raise Clair.Error.Invalid_Argument with "sigemptyset(3) failed";
      else
        declare
          error_code : constant Interfaces.C.Int := Clair.Error.get_errno;
          error_msg  : constant String           :=
            "sigemptyset(3) failed (errno: " & error_code'image & ")";
        begin
          raise Program_Error with "sigemptyset(3) failed";
        end;
      end if;
    end if;

    case new_action.kind is
      when Handle =>
        sa.sa_flags := Interfaces.C.Int (
           Interfaces.C.Unsigned (sys_signal_h.SA_RESTART) or
           Interfaces.C.Unsigned (sys_signal_h.SA_SIGINFO)
        );
        sa.uu_sigaction_u.uu_sa_sigaction := clair_signal_handler'access;
        Handler_Registry.r e g i s t e r (sig, new_action.handler);

      when Default =>
        sa.sa_flags := 0;
        sa.uu_sigaction_u.uu_sa_handler := Clair.Config.SIG_DFL;
        Handler_Registry.r e g i s t e r (sig, null);

      when Ignore =>
        sa.sa_flags := 0;
        sa.uu_sigaction_u.uu_sa_handler := Clair.Config.SIG_IGN;
        Handler_Registry.r e g i s t e r (sig, null);
    end case;

    if signal_h.sigaction2 (Interfaces.C.Int (sig), sa'access, null) /= 0
    then
      raise Program_Error with "sigaction system call failed";
    end if;
  end set_action;

  procedure dispatch is
    sig_num_c  : aliased Interfaces.C.Int;
    bytes_read : sys_types_h.ssize_t;
  begin
    loop
      bytes_read := unistd_h.read (Interfaces.C.Int (pipe_fds (0)),
                                   sig_num_c'address,
                                   Interfaces.C.Int'size / 8);
      if bytes_read > 0 then
        Handler_Registry.call (Number (sig_num_c));
      else
        -- 읽을 데이터가 없거나(EAGAIN 등) 에러 발생 시 루프 종료
        exit;
      end if;
    end loop;
  end dispatch;

begin
  if unistd_h.pipe (to_c_int_access (pipe_fds'address)) /= 0 then
    raise Program_Error with "pipe() creation failed during initialization";
  end if;
end Clair.Signal;

귀중한 시간 내어 읽어주셔서 감사하고, 어떤 피드백이든 환영입니다! 😊

추천 비추천

0

고정닉 0

0

댓글 영역

전체 댓글 0
본문 보기

하단 갤러리 리스트 영역

왼쪽 컨텐츠 영역

갤러리 리스트 영역

갤러리 리스트
번호 제목 글쓴이 작성일 조회 추천
설문 모태 솔로도 구제해 줄 것 같은 연애 고수 스타는? 운영자 25/07/21 - -
공지 프로그래밍 갤러리 이용 안내 [88] 운영자 20.09.28 45804 65
2875421 신태일 틱톡에서 실시간 떡방중이네 ㅋㅋㅋ 프갤러(175.195) 20:16 0 0
2875420 '부천김린' 틱톡에 벗방시절 박제됐음 ㅋ 짤리기전에봐라ㄱㄱ 프갤러(175.195) 20:13 0 0
2875419 70000냥 ㅇㅇ(106.101) 20:10 5 0
2875418 틱톡에 태이,우아 떡방박제됨 ㅋ 짤리기 전에 봐ㄱㄱ 프갤러(175.195) 20:09 3 0
2875417 '1 9 ) 윤공주 벗 방한대 ㄱㄱ 프갤러(175.195) 20:05 4 0
2875416 신태일 틱톡에서 실시간 떡방중이네 ㅋㅋㅋ 프갤러(175.195) 20:02 4 0
2875414 나님 위스콘신으로 예정이긴한데.. 흠.. ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:55 9 0
2875406 지진이 없으면 이미 지구는 인간이 살곳이 못된다는뜻 ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:41 8 0
2875403 모로링이 본 미래.. ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:38 12 0
2875401 시간날땐 쓰고 싶은 글이 없구 흠.. ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:36 6 0
2875400 칼럼 허벌나게 쓰고 싶을땐 허벌나게 바쁘고 ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:35 5 0
2875399 뭔가 칼럼 하나 쓰고 싶은데 눈꺼풀이 무겁당.. ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:35 6 0
2875397 역시 진짜 여름은 7월 부터당 ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:33 10 0
2875395 언어란 안 쓰면 무뎌지는 칼이당 ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:31 5 0
2875394 요즘 나님 일본어 복습중 영어는 이미 비즈니스 수준까지 가능하니 ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:31 8 0
2875392 원래 계약시간 끝나고 저녁 이후에도 달렸는데 요즘은 그냥 퍼져버림 ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:29 8 0
2875390 나님 애널 주무시기전에 칼럼 하나 쓰고 잘깡.. 흠.. ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:28 6 0
2875389 좆도그 담당아 게임 만들거면 한국 사이트를 보지마라.. [2] ㅆㅇㅆ(124.216) 19:28 35 0
2875388 와 씨발 코드랑 세팅 방법까지 그대로 다 올려주시네 [1] 루도그담당(58.239) 19:27 30 0
2875387 트럼프가 한국 좌빨들이 윤석열 대통령 건드리면 2찢명 박살낸다고 한듯? [1] ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:27 14 0
2875386 와.. ㅅㅂ 나님이 방금 뭘 본거지? 바퀴벌레 본것 같애.. ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:24 6 0
2875384 데스크탑 책상 아래 두시는분?? 질문왕(211.248) 19:21 7 0
2875383 중국애들이 존나 공유를 잘함. 한국애들은 공유 절대 안함. [2] ㅆㅇㅆ(124.216) 19:20 35 0
2875381 냥덩이 목소리 최초 공개⭐+ ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:19 10 0
2875380 주6일 햐서 5일 갈깡 주 5일에서 6일 갈깡 ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:18 6 0
2875379 주말까지 달리니 ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:17 8 0
2875378 나님 주7일 달리다 퍼져 회복루틴 ♥팬티스타킹냥덩♥갤로그로 이동합니다. 19:16 5 0
2875375 한국에서 겜회사 안드가면 AAA 게임 비디오 게임 테크닉 안가르쳐주잖아 [4] ㅆㅇㅆ(124.216) 19:13 42 0
2875374 llm 믿지 마라. 꼴통이다 나르시갤로그로 이동합니다. 19:13 11 0
2875372 언리얼이 매 달마다 FAB으로 공짜로 풀고 그다음에 작년까지만해도 [3] ㅆㅇㅆ(124.216) 19:10 27 0
2875368 진짜 마소 개발자 도구 샘플 코드 보면 존나 신기하지 않냐 [4] ㅆㅇㅆ(124.216) 19:05 36 0
2875367 언리얼 같은거 보면은 [5] 루도그담당(58.239) 19:05 42 0
2875366 너무나도 더워서 1컷 쇄골 이상은 나와도 되지 남자인데... 넥도리아(119.195) 19:05 9 0
2875363 지듣노 발명도둑잡기(118.216) 19:00 8 0
2875360 나님 소화되면 바루 주무셔야징 ♥팬티스타킹냥덩♥갤로그로 이동합니다. 18:57 9 0
2875359 칠!만!원! ㄱㄱㄱ ㅇㅇ(106.101) 18:57 7 0
2875358 지피티를 쓰면서 알기 힘든게 할 수 있는것과 없는거임 [2] ㅆㅇㅆ(124.216) 18:55 27 0
2875355 아스카는 너무 바쁘다 [6] 아스카영원히사랑해갤로그로 이동합니다. 18:53 33 2
2875353 지피티 믿고 뛰어들다가 포기함 [2] 프갤러(220.86) 18:51 27 0
2875351 돼지 비계의 비밀… ㅇㅇㅇㅇ(222.105) 18:48 9 0
2875345 멸공 ! ❤+ [1] ♥팬티스타킹냥덩♥갤로그로 이동합니다. 18:39 13 0
2875343 mysql 질문문무누문무눔 [3] 프갤러(163.152) 18:35 15 0
2875341 진짜 프로그래밍이 제일 독학하기 편하지 않냐? 말이 안됨 [6] ㅆㅇㅆ(124.216) 18:34 39 0
2875340 실무하다보니 네트워크 미들웨어 좇나 쓸모없음 뒷통수한방(1.213) 18:34 18 0
2875338 ai 이새끼들하고 정치적이야기 한국에대해서 이야기하면 존나 찢재명같더라 뒷통수한방(1.213) 18:30 14 0
2875331 앱히키 씨발년아 보고 있는 거 안다. 근황 보고해라 프갤러(222.108) 18:14 14 0
2875329 실무하다보니 느끼는데 그냥 네트워크 구조나 미들웨어 지식이 더중요한거같다 [1] ㅇㅇ(211.234) 18:13 25 0
2875327 공수처 검찰청 경찰청 국제수사 과학수사 포랜식수사 기무사 국정원 존재이유 뒷통수한방(1.213) 18:10 11 0
2875323 2찢명 이 극좌새끼 때문에 대한민국 망하는거 확정 이민 준비나 해라 [1] ♥팬티스타킹냥덩♥갤로그로 이동합니다. 18:03 19 0
뉴스 '핑계고' 스테이씨, 유재석∙조세호도 춤추게 한 'I WANT IT'…데뷔 5년 차 바이브로 예능 텐션 올킬 디시트렌드 07.25
갤러리 내부 검색
제목+내용게시물 정렬 옵션

오른쪽 컨텐츠 영역

실시간 베스트

1/8

뉴스

디시미디어

디시이슈

1/2