[Type: Refactoring]
[Scope: src/Tizen.Uix.InputMethod]
[Priority: 🔴 Critical]
[Lens: Performance, Coding Guidelines]
Observation
InputMethodEditor의 네이티브 문자열/구조체 메모리 해제가 세 곳에서 누락되어 있습니다. 이 모듈의 Interop에는 free() 바인딩(Interop.Libc)이 아예 존재하지 않아, 네이티브가 caller-owned로 반환하는 메모리를 해제할 수단 자체가 없습니다.
1) GetSurroundingText (InputMethodEditor.cs:1841-1851)
public static void GetSurroundingText(int maxLenBefore, int maxLenAfter, out string text, out int cursorPosition)
{
IntPtr txt;
ErrorCode error = ImeGetSurroundingText(maxLenBefore, maxLenAfter, out txt, out cursorPosition);
...
text = Marshal.PtrToStringAnsi(txt); // txt 를 free() 하지 않음
}
네이티브 ime_get_surrounding_text()는 text를 호출자가 free()로 해제해야 하는 계약입니다(Tizen native API 문서 remarks). 현재 관리 문자열로 복사만 하고 원본 버퍼를 방치합니다.
2) GetSelectedText (InputMethodEditor.cs:2078-2088) — ime_get_selected_text() 동일 계약, 동일 누수.
3) UpdatePreEditString (InputMethodEditor.cs:1727-1775) — 성공 경로 미해제:
try
{
foreach (PreEditAttribute attribute in attrs)
{
IntPtr attr = Marshal.AllocHGlobal(Marshal.SizeOf(imePreEditAttribute)); // 16B/attr
allocatedAttrs.Add(attr);
...
glist = Interop.GLib.GListAppend(glist, attr); // +GList 노드/attr
}
ErrorCode error = ImeUpdatePreeditStringWithGlist(str, glist);
if (error != ErrorCode.None) { ... throw ...; }
// 성공 시: allocatedAttrs / glist 해제 없이 반환 → 전량 누수
}
catch
{
// 실패 경로에서만 FreeHGlobal + GListFree 수행
throw;
}
catch 경로에서는 해제하면서 성공 경로에서는 해제하지 않는 비대칭 구조입니다. 네이티브가 소유권을 가져간다면 catch 경로의 해제가 double-free이고, 가져가지 않는다면(네이티브는 호출 중 IPC 직렬화 후 리스트를 소유하지 않음) 성공 경로가 누수입니다 — 두 경로 중 하나는 반드시 잘못되어 있으며, 네이티브 계약상 성공 경로 누수입니다.
Problem
- Performance 렌즈: IME는 서라운딩 텍스트를 커서 이동/키 입력마다 조회하고, 프리에딧은 조합 중 매 키 입력마다 갱신되는 핫 패스입니다. 호출당 수십 바이트씩 프로세스 수명 내내 무한 누적되는 네이티브 힙 누수입니다. IME는 상시 구동 프로세스라 누적 효과가 특히 큽니다.
- Coding Guidelines 렌즈: 네이티브 out-string 계약(caller frees) 위반. TizenFX 타 모듈(예: Tizen.Network.Smartcard의
Interop.Libc.Free)이 따르는 표준 패턴 미적용. 성공/실패 경로의 자원 관리 비대칭은 try/finally로 정상화해야 합니다.
Proposed Improvement
1. Interop/Interop.Libc.cs 신규 추가 (Smartcard 등 기존 모듈과 동일 패턴):
internal static partial class Interop
{
internal static partial class Libc
{
[DllImport(Libraries.Libc, EntryPoint = "free")]
internal static extern void Free(IntPtr ptr);
}
}
2. 문자열 반환 2곳 해제 (After — GetSurroundingText 예):
IntPtr txt = IntPtr.Zero;
ErrorCode error = ImeGetSurroundingText(maxLenBefore, maxLenAfter, out txt, out cursorPosition);
if (error != ErrorCode.None) { ... throw ...; }
try
{
text = Marshal.PtrToStringAnsi(txt);
}
finally
{
if (txt != IntPtr.Zero)
Interop.Libc.Free(txt);
}
GetSelectedText도 동일 적용.
3. UpdatePreEditString — catch 해제를 finally로 이동:
try
{
... alloc + GListAppend ...
ErrorCode error = ImeUpdatePreeditStringWithGlist(str, glist);
if (error != ErrorCode.None) { ... throw ...; }
}
finally
{
foreach (IntPtr attr in allocatedAttrs)
Marshal.FreeHGlobal(attr);
if (glist != IntPtr.Zero)
Interop.GLib.GListFree(glist);
}
Target Files
src/Tizen.Uix.InputMethod/Tizen.Uix.InputMethod/InputMethodEditor.cs
src/Tizen.Uix.InputMethod/Interop/Interop.Libc.cs (신규)
src/Tizen.Uix.InputMethod/Interop/Interop.Libraries.cs (Libc 상수 추가)
Expected Impact (Quantitative Metrics)
GetSurroundingText: 호출당 strlen(text)+1 바이트 누수 → 0. 예: 평균 64B 컨텍스트, 분당 200회 입력 시 ~12.8KB/분 누적이 제거됨.
GetSelectedText: 호출당 선택 텍스트 길이+1 바이트 누수 → 0.
UpdatePreEditString: 조합 중 키 입력당 attr 1개 기준 40B(구조체 16B + GList 노드 ~24B) 누수 → 0.
- 상시 구동 IME 프로세스의 네이티브 힙 무한 증가 요인 3곳 제거.
API Compatibility Check
- Public API signature change: 없음 (메서드 본문 내부 자원 해제만 추가)
- Behavior change: 없음 (반환값/예외 동일; 누수만 제거)
- Tizen API Level floor: 유지 (신규 .NET API 미사용, libc free 바인딩은 플랫폼 공통)
Impact Scope
- 수정 심볼 호출부: 모듈 내부 3개 메서드 본문 한정 (rg 기준 저장소 내 참조는 본 모듈 8곳 + Tizen.NUI의 동명 이종 클래스 — NUI 측은 무관)
- 다른 어셈블리 영향: 없음
[Type: Refactoring]
[Scope: src/Tizen.Uix.InputMethod]
[Priority: 🔴 Critical]
[Lens: Performance, Coding Guidelines]
Observation
InputMethodEditor의 네이티브 문자열/구조체 메모리 해제가 세 곳에서 누락되어 있습니다. 이 모듈의 Interop에는free()바인딩(Interop.Libc)이 아예 존재하지 않아, 네이티브가 caller-owned로 반환하는 메모리를 해제할 수단 자체가 없습니다.1)
GetSurroundingText(InputMethodEditor.cs:1841-1851)네이티브
ime_get_surrounding_text()는text를 호출자가free()로 해제해야 하는 계약입니다(Tizen native API 문서 remarks). 현재 관리 문자열로 복사만 하고 원본 버퍼를 방치합니다.2)
GetSelectedText(InputMethodEditor.cs:2078-2088) —ime_get_selected_text()동일 계약, 동일 누수.3)
UpdatePreEditString(InputMethodEditor.cs:1727-1775) — 성공 경로 미해제:catch 경로에서는 해제하면서 성공 경로에서는 해제하지 않는 비대칭 구조입니다. 네이티브가 소유권을 가져간다면 catch 경로의 해제가 double-free이고, 가져가지 않는다면(네이티브는 호출 중 IPC 직렬화 후 리스트를 소유하지 않음) 성공 경로가 누수입니다 — 두 경로 중 하나는 반드시 잘못되어 있으며, 네이티브 계약상 성공 경로 누수입니다.
Problem
Interop.Libc.Free)이 따르는 표준 패턴 미적용. 성공/실패 경로의 자원 관리 비대칭은 try/finally로 정상화해야 합니다.Proposed Improvement
1.
Interop/Interop.Libc.cs신규 추가 (Smartcard 등 기존 모듈과 동일 패턴):2. 문자열 반환 2곳 해제 (After —
GetSurroundingText예):GetSelectedText도 동일 적용.3.
UpdatePreEditString— catch 해제를 finally로 이동:Target Files
src/Tizen.Uix.InputMethod/Tizen.Uix.InputMethod/InputMethodEditor.cssrc/Tizen.Uix.InputMethod/Interop/Interop.Libc.cs(신규)src/Tizen.Uix.InputMethod/Interop/Interop.Libraries.cs(Libc 상수 추가)Expected Impact (Quantitative Metrics)
GetSurroundingText: 호출당strlen(text)+1바이트 누수 → 0. 예: 평균 64B 컨텍스트, 분당 200회 입력 시 ~12.8KB/분 누적이 제거됨.GetSelectedText: 호출당 선택 텍스트 길이+1 바이트 누수 → 0.UpdatePreEditString: 조합 중 키 입력당 attr 1개 기준 40B(구조체 16B + GList 노드 ~24B) 누수 → 0.API Compatibility Check
Impact Scope