Friday, March 31, 2017

[Repost] Cô gái trên cầu Long Biên

Post lại từ ngày 6 tháng 1 năm 2017. Sau lần đi bộ trên cầu Long Biên trong chuyến đi Hà Nội

Ký sự thanh niên đi bộ qua cầu Long Biên:
Thanh niên đang tức tối về service của chả cá Lã Vọng liền đi lang thang đến đầu phố hàng cót, đua đòi mua ly nước + bịch khoai lắc cho giống local, ngồi thấy cây cầu, hỏi bé bán hàng lên cầu long biên như nào, bé chỉ đường kèm ánh mắt ái ngại cho mình. Thấy mình cầm bản đồ hn + nói giọng bắc, chắc bé nghĩ mình ở bản mới xuống miền xuôi đi chơi nên bé cũng lắc đầu ngao ngán mà chỉ đường cho đi. Đi đến 1/4 cây cầu, thấy cũng hơi xa, định quay về thì bỗng thấy bé (gái) đi trước, mình liền nới sải chân dài ra, chắc bé cũng sợ nên lấy đt ra gọi vu vơ. Đến khoảng 1/3 cầu thì mình mở lời "đi chung cho đỡ buồn bạn ơi" bé quay đầu lại cười và... ôi muệ ơi con gái nhà ai mà xinh thế <3 hỏi thăm qua loa vớ vẩn vài câu thì biết bạn ấy đi lên cầu tụ tập nướng ngô với bạn bè, bạn ý có mời mình tham gia chung cho vui, nhưng thấy cậu con trai đi cùng ko thiện chí lắm nên mình chữa ngựong "mình còn phải đi hết cây cầu này rồi về, các bạn cứ tự nhiên". Thật ếu có cái ngu nào bằng cái ngu sĩ diện, đường cầu còn hơn nửa đi sợ bỏ mie, sợ một phần giang hồ hiểm ác, sợ một phần vong ma hãm hại (nghe nói cầu long biên có lắm người tự tử). Vừa đi tay vừa nắm chặt cây xiên của bịch khoai lắc, có biến là sẵn sàng chơi ngay. Chưa kể ba lô còn nguyên củ tỏi merlin chưa dùng. Sau một hành trình dài nghĩ vu vơ cũng đã đến chân cầu bên kia (q long biên). Nghĩ giờ đi lại hết một lần cây cầu nữa để về lại hoàn kiếm chắc stress nặng. Nghĩ rồi thanh niên chạy đến 2 ông xe ôm, thấy mặt 2 ông hơi giống đại bàng trong phim cssh, tự hỏi "có khi nào ông này đang chạy rồi xử mình ko@@?" tay hắn vẫn cầm cây xiên mặc dù khoai hắn đã ăn hết rồi. Ông xe ôm chở qua cầu, hỏi han vớ vẩn gì đấy mới biết ông có quê ngoại ở sg. Lúc xuống xe ông còn chỉ dẫn tận tình đi hộ ntn để về phố Hàng Đường. Vậy mà thanh niên vẫn nắm chặt cây xiên @@! Hết rồi ạ.

Wednesday, March 29, 2017

Thread-safe and reentrant function

Trong lập trình, cụ thể trong embedded C domain, các thuộc tính của một function ngoài các arguments, giá trị return còn có 2 khái niệm nữa khá phức tạp là Synchonous/Asynchronous, reentrancy và threadsafe. Trong bài này mình tập trung nói về khái niệm reentrancy.

Nói một cách hàn lâm, một hàm được gọi là "thread safe" nếu nó bị interrupt giữa function và khi trở lại, nó vẫn safely hoạt động như mong đợi.

Ví dụ một vài hàm threadsafe:

int my_adder( int a , int b)
{
    return (a + b);
}

Hàm trên được gọi là threadsafe bởi vì dù bạn có interrupt nó ở đâu thì kết quả của my_adder(10,11) luôn luôn là 21.

Vậy hàm như thế nào được gọi là non safe. Mình sẽ ví dụ một use case cụ thể trong embedded về vấn đề threadsafe.

Giả sử mình cần viết ứng dụng hiển thị đồng hồ phút và giây. Mình có một timer đếm giây, mỗi giây timer sẽ được trigger và đếm tăng counter lên 1.

/* Start of File */
#include <somefile.h>

/* Global variable */
uint32_t count_second = 0;

/* define ISR for timer */ 
ISR void timer( void )
{
    if(count_second == 3600)
    {
        count_second = 0;
    }
    else
    {
        count_second = count_second+1;
    }
}

void show_clock( void );

void main( void )
{
    init_timer( some_configuration );
    init_uart( for_displaying_clock );
    while( 1 )
    {
        show_clock();
    }
}

void show_clock( void );
{
    uint32_t minute;
    uint32_t second;

    minute = count_second/60;

    /* Timer ISR may happen here */   

    second = count_second%60;
    printf(" It is %d:%d", minute, second");
}

/* End of File */

Bạn nghĩ chương trình trên chạy ổn chứ? Absolutely not,

Giả sử như biến count_second đang có giá trị là 59, theo như logic, thì kết quả đồng hồ phải chuyển từ ... 00:58  >>  00:59  >>  1:00  >>..., đúng không?

Đúng, theo như expectation thì đồng hồ phải hiển thị như vậy, nhưng vẫn có trường hợp, với code kiểu tiên, tôi dám chắc là các bạn từng viết chương trình này đã gặp những giá trị lạ n

00:58  >> 00:59 >> 00:00 > 1:00 tại sao lại có điều đó xảy ra.

Bạn hãy để ý dòng "/* Timer ISR may happen here */"

Lúc counter_second đang có giá trị là 59 thì minute=59/60 = 0;

lúc interrupt xảy ra, giá trị counter_second sẽ là 60 và 60%60 sẽ là 0 nên second sẽ là "00", khi chạy lệnh print, 2 giá trị minute và second sẽ là 0:00. Và hàm này (hàm show_clock) không được gọi là threadsafe, bởi vì khi nó bị ngắt và trở lại, nó đã không hoạt động đúng như mong đợi nữa. Vậy có cách nào để hàm trên trở thành threadsafe, có nhiều cách:

  • Cách #1: lưu giá trị biến toàn cục vào local variable, với cách này (tạm coi như lệnh gán là một lệnh trông thể bị interrupt), hàm này luôn đảm bảo 2 biến minute và second luôn đồng nhất với count_second và vấn đề trên sẽ được giải quyết.
void show_clock( void );{
    uint32_t minute;
    uint32_t second;



    uint32_t t_second_count;

    t_second_count = count_second;
      minute = count_second/60;

    /* Timer ISR may happen here */   

    second = count_second%60;
    printf(" It is %d:%d", minute, second");
}

  • Cách #2: ngắt interrupt tại nơi mà interrupt có thể làm sai lệch hoạt động của hàm, với cách này, interrupt sẽ không thể chen giữa 2 lệnh gán và sẽ không có cơ hội cho giá trị lạ xuất hiên.
 void show_clock( void );{
    uint32_t minute;
    uint32_t second;

    díable_interrupt( void );     minute = count_second/60;

    /* Timer ISR may NOT happen here */   

    second = count_second%60;
    enable_interrupt( void );

    printf(" It is %d:%d", minute, second");
}

Hi vọng các bạn có thể hiểu được phần nào về khái niệm threadsafe. Còn về reentrancy, khơi khó tưởng tượng hơn một chút.

Một function được gọi là reentrancy khi nó có thể gọi lại lần nữa trước khi kết thúc lần chạy đầu tiên.

Ví dụ mình có một hàm làm nhiệm vụ là double giá trị của một biến

#include "somefile.h"

uint32_t my_var=2;

void my_double( uint32_t *arg)
{
    uint32_t temp;
    temp = *arg;
    /*Interrupt may happen here*/
    temp = temp + *arg;
    *arg = temp;
}

ISR someInturrupt( void )
{
   my_double( &my_vả );
}

Hàm này sẽ được gọi trong hàm main mà trong một interrupt:

Giả sử giá trị của my var hiện tại đang là 2, trong trường hợp bình thường, hàm main gọi my_double( &my_var ) thì my_var sau đó phải có giá trị là 4. 

Nhưng không may, trong lúc đay chạy hàm my_double, một interrupt xảy ra và trong interrup, hàm my_double được gọi ngay giữa hàm ( như chú thích), lúc này, hàm inturrupt chạy xong và biến var có giá trị là 4, và temp của lần gọi đầu tiên có giá trị là 2 + với *my_var là 4, và cuối cùng, kết của của biến var là 6, đã mất đi ý nghĩa của hàm my_double.

cách giải quyết vấn đề trên cũng giống như thread safe là disable interuppt những chỗ có thể gây hư hại cho hàm my_double.

Và nhìn chung thì, hàm thread safe luôn luôn reentrant, nhưng hàm reentrant chưa chắc đã threadsafe. Vấn đề này các bạn có thể google thêm, mình chỉ cố gắng đưa ra khái niệm để các bạn hiểu được concept.

Trong thực tế, theo mình thấy thì người ta luôn cố gắng để function trở thành threadsafe hoặc reentrant nếu cần thiết, đương nhiên là chỉ nếu thôi, vì có những trường hợp reentrant là quá xa xỉ với một function, ví dụ như những function luôn chỉ được gọi một lần trong chương trình (initialization) chẳng hạn thì không cần reentrant, vì đâu có hàm nào gọi lại function ấy lần 2. Còn threadsafe,  rõ ràng là để đảm bảo tính đồng nhất (data consistency như trong ví dụ 1), thì phải cố gắng bảo vệ nhũng vùng data dùng chung để cho hàm trở thành threadsafe.

    

[Self Study] Haskell - day#0

Khi bế tắc, việc đầu tiên thanh niên nghĩ đến là học thứ gì đó mới mẻ và fun, và thanh niên đã nghĩ tới Haskell, một ngôn ngữ lập trình hướng hàm (Functional Programming Language), với cấu trúc khác hẳn các ngôn ngữ mà thanh niên hay dùng trong công việc, let him show his journey.
---
Environment:
Thanh niên học haskell trên môi trường linux (Arch linux)
---

Day0: Thanh niên tìm hiểu functional programming trên wiki, đọc sơ sơ để biết về nó, xong tìm hiểu overview về Haskel, tiếp tục thanh niên search từ khóa "learn haskell the hard way" và đọc từ trên xuống dưới.

http://yannesposito.com/Scratch/en/blog/Haskell-the-Hard-Way/#a-type-example

Vừa đọc, thanh niên vừa tìm cách cài tool "ghc" (Haskell compiler), "runhaskel" (run haskell như một script", ghci (haskell command line).

Đọc sơ sơ hắn đã biết Haskell rất khó học, nó khó một phần là do nó là Functional Programming Language (FP), một phần vì Haskell là ngôn ngữ thuần FP, bản thân FP đã khó học rồi, Haskell còn đặc biệt hơn vì nó là ngôn ngữ 100% FP, có một số ngôn ngữ khác cũng là FP, nhưng nó vẫn hỗ trợ imperative, nhưng Haskell thì không. Nó là một ngôn ngữ chỉ dành cho ai muốn học một thứ hoàn toàn khác biệt, không lai tạp, và nó rất hợp với hoàn cảnh của thanh niên lúc này.

Rất nhiều term mới đến với thanh niên, nhưng thanh nhiên bỏ qua, một cách từ tốn, thanh niên gõ những dòng đầu tiên để học về nó, hắn tạo một file mới tên hello.hs có nội dung như bên dưới,

main = print "Hello World!"


Sau đó hắn dùng ghc để build file hello.hs, và file executable tên hello đã được tạo ra
Voila, nó đã chạy, làm phức tạp vấn đề hơn một chút,

main = do
    print "Who are you?"
    yourname <- getLine
    print ("I am " ++ yourname)

Ok, nó đã chạy.


Cảm thấy bài viết trên trang yannesposito hơi cụt, với lại tác giả cũng nói rõ tác giả chỉ giới thiệu overiew thôi nên thanh niên tiếp tục với một trang web khác (http://learnyouahaskell.com/ Learn you a haskell), sau một lúc lại introduction về haskell, và thanh niên lại tiếp tục hành trình Starting out.

Trước khi đến với starting out, cần nhắc lại là ở phần introducotion của trang learnyouahaskell có nhắc đến cách setup môi trường để học Haskell cho hiệu quả:
  • Tạo riêng một thư mục làm việc
  • Trong thư mục đó tạo một file có tên .hs, chính là nơi lưu giữ source code bạn sẽ viết
  • Mở một commandline session trong thư mục đó (Trong linux thì đương nhiên, trong window là cmd thôi) và chạy lệnh ghci (chính là commandlined của Haskell), trong dấu nhắc "Prelude>" của ghci, chạy lệnh ":load filename.hs" để compile và load nội dung code haskell của bạn lên, cho những lần sau, mỗi khi thay đổi source code, chỉ cần gõ ":r" tại dấu nhắc của ghci.
Vậy là setup xong môi trường.

Bắt đầu với ghci, với các lệnh cộng trừ nhân chia cơ bản, không có gì đặc biệt, như bao ngôn ngữ khác.

Các câu lệnh logic and (&&), or (||), và "not". Các câu lệnh so sánh "==" và "/=", một điều lưu ý là với haskell, 2 vế của phép so sánh phải cùng type (type là gì mai mốt học sẽ biết). Haskell có khả năng so sánh 2 list số hoặc chữ.

Tiếp theo là một số hàm có sẵn của haskell:
  • max a b: lấy max(a,b) lưu ý cách gọi hàm của haskell là "function arg"
  • min a b: lấy min(a,b)
  • lưu ý với haskell hàn có độ ưu tiên hơn phép tính ví dụ "max 5 2*3" sẽ tương đương với "(max 5 2)*3" tức là 15, không phải "max 5 (2*3)" tức là kết quả là 6. Nên thử ngay để kiểm chứng lời thanh niên nói.
  • Có một điểm khá thú vị của haskell là khả năng linh hoạt trong cách viết cú pháp, ví dụ chúng ta có hàm div a b, lấy a chia b rồi trả ra phần nguyên, chúng ta có thể viết theo 2 cách "div 19 8" hoặc "19 `div` 8"
Next, chúng trang learyouahaskel sẽ hướng dẫn chung ta cách viết một vài hàm đơn giản, cách khai bao hàm của haskell khá đơn giản, trong haskell, definition và declaration của một hàm là một, và đặc biệt hơn, không giống như C, hàm của haskell không phân biệt thứ tự, trong source code haskell, bạn có thể define hàm foo trước hàm bar, nhưng trong hàm foo hoàn toàn có thể gọi hàm bar, nếu trong C, bạn sẽ bị báo lỗi "definition of sth is not found"

Next, cú pháp if else thần thánh

foo x = ( if x< 100
then DO_STH
else DO_STH_ELSE)

DO_STH_ELSE trong Haskell là bắt buộc.

Tổng kết ngày đầu tiên: cú pháp haskell khá đơn giản, thiên nhiều về toán học, chưa thấy điểm gì đặc biệt của ngôn ngữ haskell.
--




Monday, March 27, 2017

Plan di campuchia

Plan đi du lịch campuchia

Objective:  biết một vùng đất mới, giải trí thư giãn sau những ngày làm việc căng thẳng, học hỏi một nền văn hóa khác.Constrain: Chi phí dưới 4.5Tr, kéo dài trong vòng 4 ngày.

Chọn hành trình đi Sihanoukville vì cảnh đẹp, tương đối thanh bình so với Phnompenh, an toàn hơn so với Angkor Wat.

Phương tiện di chuyển: Bus, feet, tàu biển.

Hành trình:
Tối T4: Bắt xe Kumho đi Hà Tiên buổi tối, giá vé tham khảo loại ghế nằm là 175K/người, đến Hà Tiên có xe trung chuyển miễn phí đến cửa khẩu, cách bến xe Hà Tiên 10km.

Sáng T5: Đến cửa khẩu Xà Xía vào buổi bình minh, những tia nắng chói chang xuyên qua những tán cây. Cửa khẩu làm việc lúc 6h30, hopefully, chúng ta có thể làm thủ tục xong trước 7h30, sau đó bắt xe Đi Sihanoukville (giá tam khảo $14/người) http://hatiensihanoukville.com/home/index.php/shops/Xe-Di-Sihanouk-Ville/Xe-Bus-Di-Campuchia-Xe-Ha-Tien-Di-SIhanoukVille-77.html

Xe đi tầm 4 tiếng, đến Sihanouk vào buổi trưa, nếu đến sớm thì tắm biển rồi ăn trưa và nhận KS, nếu ko thì bỏ phần tắm biển, phòng KS ở Sinanouk khá rẻ, có thể book trên Agoda.

Nhận phòng KS xong nghỉ ngơi, chiều tối dạo khám phá Sihanouk, ăn hải sản ở bờ biển, chém gió, ngắm gái, tâm sự chuyện đời các kiểu, kể chuyện tình lâm ly bi đát, xong về KS nghỉ ngơi.

Sáng T6: Tập thể dục, boxing các kiểu, xong mua vé tàu Party boat (vé khứ hồi là 20$) đi Koh Rong Samloem. Đến Kohrong Samloem, khám phá đảo, chiều tính đường đi ăn ở Lazy beach resort, giá khoảng 1$/1 món.Mang theo đèn pin. Ăn no say thì về KS ngủ, tiết mục tâm sự chuyện đời tư bắt đầu. giá phòng KS là TBD

Sáng T7: Tạm biệt KohRong Samloem lúc 9h sáng, về đến Sihanouk khoảng 10h, ăn trưa rồi nghỉ ngơi, chờ đến 12h bắt xe đi phnoupenh. Đến thủ đô của Vương quốc Campuchia lúc 4 chiều (hơi cập rập khoản khách sạn), nhận phòng và thăm thú thủ đô Phnompenh vào buổi tối. 

Sáng CN (optional): thăm thú nhẹ nhàng Phnompenh, xong bắt xe về HCM, chia tay và hẹn gặp lại tại weekly meeting sáng hôm sau.