30 Nisan 2015 Perşembe

C++ Notlarım POD

Default Initialization Nedir?
Default Initialization yazısına taşıdım.

Zero Initialization Nedir?(C++98)
Bir değişkene 0 değerinin atanmasıdır.

Value Initialization Nedir? (C++03)
Value Initialization yazısına taşıdım.

POD Nedir?
POD C gibi yazılan, constructor, destructor, virtual metod gibi C++'a mahsus hiçbir şey içermeyen struct veya sınıflara verilen isimdir. Member method POD olmasına engel değildir.

POD Örnekleri
1. Static member POD olmasına engel değildir.
struct POD
{
  int x;
  char y;
  void f() {} //no harm if there's a function
  static std::vector<char> v; //static members do not matter
};
2. Destructor POD olmasına engeldir.
struct AggregateButNotPOD1
{
  int x;
  ~AggregateButNotPOD1(){} //user-defined destructor
};
3. non-POD member variable POD olmasına engeldir
struct AggregateButNotPOD2
{
  AggregateButNotPOD1 arrOfNonPod[3]; //array of non-POD class
};
4. Farklı access modifier kullanılması POD olmasına engeldir.
struct B {
public:
    int m1;
private:
    int m2;
};

C++ Kodlarında POD'un Yeri Nedir?
POD tanım olarak sadece veridir. Nesneye yönelik programlamanın temeli veri artı davranış üzerine oturtulmuştur. Bu yüzden C++ programlarımda pod en çok state taşımak için kullanılır. Örneğin socket üzerinden veri göndermek veya Java'daki serializable nesneleri gibi bir yere yazılıp okunma işleri için uygundurlar.

memcpy ve POD
Veri taşırken bazen veriyi memcpy ile sockete, shared memory alanına yazmak gerekir. POD tipler memcpy ile kullanılabilir. Örnek:

#define N sizeof(T)
char buf[N];
T obj; // obj initialized to its original value
memcpy(buf, &obj, N); // between these two calls to memcpy,
// obj might be modified
memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type
// holds its original value

Konuyla ilgili olarak Data Alignment başlıklı yazıya göz atabilirsiniz.


POD Kontrolü
Bir tipin POD olup olmadığını is_pod kullanarak aşağıdaki gibi öğrenebiliriz.
#include <type_traits
std::is_pod <T>::value;

C++11 ile POD
Eski C++ standardındaki POD kavramı biraz katıydı. C++11 ile POD kavramı 2 alt kavrama daha ayrıldı. Bunlar trivial class ve standart layout class. Standart layout diğer dillerle iletişim için var.

Trivial class aynı zamanda trivally copyable kavramınıda içerir. Yani superset'idir.

Yeni kavramlar için yeni metodlar da eklendi.

template <typename T>
struct std::is_pod;
template <typename T>
struct std::is_trivial;
template <typename T>
struct std::is_trivially_copyable;
template <typename T>
struct std::is_standard_layout;
Trivial Class
Artık memcpy ile bir sınıfı kullanmak için trivial class olması yeterli. POD'a göre gevşetilen kurallar şöyle.
1. Artık tüm member'lar aynı access modifier seviyesinde olmak zorunda değil.
struct Trivial4 {
public:
    int a;
private: // no restrictions on access modifiers
    int b;
};
2. Static değişkenler non-Trivial olabilir.
struct Trivial8 {
     int x;
     static NonTrivial1 y; // no restrictions on static members
}
3. Default constructor olması yeterli.
struct Trivial9 {
     Trivial9() = default; // not user-provided
      // a regular constructor is okay because we still have default ctor
     Trivial9(int x) : x(x) {};
     int x;
}
Standart Layout
Bu konuyu sonra yazacağım.

POD Initialization
Default Initialization
Default initialization ile POD içindeki field'lar rastgele değerler ile doldurulur. Örnek:

MyPodClass instance2;//Fields are garbage
MyPodClass* instance3 = new MyPodClass;//Fields are garbage

POD ve Zero Initialization
POD'ların 0 ile doldurulması için aşağıdaki örneğe dikkat etmek gerekir.

//Struct does not have a constructor
struct X {
 int field;
}
X x; //Default initialized. Not zero initialized
X x = {}; // Zero initialized
X x = X(); // Zero initialized
X* x = new X () // Zero initialized

Pod Dizisi ve Zero Initialization
POD'lardan oluşan bir diziyi 0 ile doldurmak için memset kullanılabilir.
 
struct X {};
X myArray [10]; //array'deki elemanlar default initialize edilir
memset (myArray,0,sizeof(myArray)); //array'deki elemanlar 0 ile doldurulur

Bir başka yöntemde ise struct'a default constructor ekleniyor. Ama bu sefer yapı POD olmuyor. Sadece örnek olsun diye ekledim.
struct X {
  Test (){}//Zero Initialize all members
private:
  X (const X&);
  X& operator=(const X&);
          }

Non-POD ve Initialization

Primitive Dizi ve Zero Initialization
Primitive diziler aşağıdaki gibi zero initialize edilebilir.
int array[100] = {};
Döngü ile 0 atamak veya memset yapmak arasındaki hız farkını bilmek mümkün olmayabilir.

Non-POD Value Initialization
Örnekte non-pod yapının primitive alanları value initialization ile ilklendirilir.

struct Struct {
    Struct() : memberVariable() {}
    int memberVariable;
};

Java ve Default Initialization
Java bir sınıf içindeki field'ları constructor çağırılmadan önce default değerleri ile ilklendirir.

C# ve Default Initialization
C# bir sınıf içindeki field'ları constructor çağırılmadan önce default değerleri ile ilklendirir. Default değerin ne olduğu burada yazıyor. C++'takinin aksine default değerler garbage değiller. Scalar tipler için default değer 0'dır.
  • For a variable of a value-type, the default value is the same as the value computed by the value-type's default constructor (Section 4.1.1).
  • For a variable of a reference-type, the default value is null.



24 Nisan 2015 Cuma

NonBuffered ve Buffered I/O

Bu yazı ile ilgili olarak Devpts başlıklı yazıyı okuyabilirsiniz.Orada da anlatıldığı gibi her process 3 tane posix stream ile başlatılıyor. Ancak bir çok programlama dili işletim sistemi tarafından atana bu posix streamlerin de üzerine başka soyutlamalar getiriyor. Bu yazıda bunlara kısaca bakacağız.

Not : I/O işlemlerinde eğer disk dolarsa ENOSPC hata kodu döndürülür.

Non-Buffered I/O

Non-Buffered I/O için işletim sistemi tarafından sağlanan open() metodu kullanılabilir Örnek :
open(filename, O_RDRW | O_CREAT, 0644);
open aslında 
int open(const char *pathname, int flags) ve
int open(const char *pathname,int flags, mode_t mode);
şeklinde iki imzaya sahip. 3 parametreli metod yani mode parametresi alan metod, flags alanında O_CREATE varsa dosyanın hangi haklarla yaratılacağını belirtmek için kullanılıyor. Yani özeti şu cümle.
mode specifies the permissions to use in case a new file is created.

Page Cache'in Kullanılmaması (Virtual Memory kullanılmaması)

Page Cache, the Affair Between Memory and Files başlıklı yazıda da anlatıldığı gibi non-buffered I/O yapsak bile işletim sistemi halen page cache yapısını kullanmaya devam etmekte. Bu sayfadaki page cache yapısını anlatan şekil aşağıda.



Eğer bu yapıyı da devre dışı bırakıp direkt diske yazmak istersek Linux üzerinde O_DIRECT seçeneği kullanılabilir. Bu durumda Page Cache kullanılmıyor ancak Disk Write Cache halen kullanılıyor.
Burada O_DIRECT açıklanmış.

 O_DIRECT'e karşılık olarak Windows'ta FILE_FLAG_NO_BUFFERING  (önbellekte tutma) seçeneği kullanılabilir.


Small file not committed to disk for over a minute sorusunda da açıklandığı gibi farklı dosya sistemleri farklı cache ayarları kullanabiliyorlar.


Bir dosyanın O_DIRECT seçeneği ile açılıp açılmadığını görmek için How to tell if a given process opened files with O_DIRECT? sorusunun cevabına göz atabilirsiniz. Bu bayrağın kullanılması diske yazma işleminde tüm verinin tamamen yazıldıktan sonra write() metodunun dönmesini garanti etmez! How are the O_SYNC and O_DIRECT flags in open(2) different/alike? sorusunda da bu durum açıklanıyor.

Verinin Diske Yazıldığını Garanti Etmek (Disk Write Cache kullanılmaması)
O_SYNC bayrağı ile verinin diske yazıldığını garanti etmek man sayfasına göre mümkün  çünkü man sayfasında şu cümle geçiyor. 
O_SYNC The file is opened for synchronous I/O. Any write(2)s on the resulting file descriptor will block the calling process until the data has been physically written to the underlying hardware.
Örnek : 
fd = open(filename, O_SYNC);
O_SYNC'e karşılık olarak Windows'ta FILE_FLAG_WRITE_THROUGH kullanılabilir. FILE_FLAG_WRITE_THROUGH seçeneğinin FILE_FLAG_NO_BUFFERING ile kullanılıp kullanılmaması aşağıda açıklanmış.

Ancak HDD, FS, O_SYNC : Throughput vs. Integrity yazısında da açıklandığı gibi HDD de kendi içinde bir önbellek kullanıyor. O_SYNC bayrağı kullanılsa bile bu verinin tamamen manyetik ortama yazıldığını garanti etmiyor.

POSIX guaranteeing write to disk sorusunda ise synch() metodunun kullanılması söylenmiş.

posix_fadvise metodu
Dosya açıldıktan sonra posix_fadvise metodu ile bir çok ince ayar daha yapmak mümkün. Örneğin 

POSIX_FADV_RANDOM :  Bu bayrak ile dosyaya rastgele bir şekilde erişileceğini belirtip işletim sistemi 
tarafından kullanılan read-ahead (önden okuma özelliği kapatılabilir).

POSIX_FADV_SEQUENTIAL : Bu bayrak ile dosyaya daha fazla read-ahead yapılması gerektiği belirtilir.
posix_fadvise(in_fd, 0, 0, POSIX_FADV_SEQUENTIAL);
POSIX_FADV_WILLNEED: İşletim sistemine read-ahead (önden okuma) işlemini ne kadar yapması söylenebilir. Örnek.
posix_fadvise(fd, 0, 512*1024*1024, POSIX_FADV_WILLNEED);
POSIX_FADV_DONTNEED: İşletim sistemine read-ahead için ayrılan bellek alanına ihtiyaç kalmadığı söylenir.

Büyük Dosyaları Okumak

Non-Buffered I/O ile Kullanılan Bazı Metodlar

close metodu
close metodu dosyayı kapatır. Burada anlatıldığı gibi bu metodun döndürdüğü değere mutlaka bakılması tavsiye ediliyor.
Eğer zaten kapatılmış olan bir dosyayı, tekrar kapatmaya kalkışırsak EBADF hatası alırız. Örnek:

fgets ve gets
gets() güvenli bir metod değil, bu yüzden mümkünse fgets() kullanılmalı.

Since the user cannot specify the length of the buffer passed to gets(), use of this function is discouraged. The length of the string read is unlimited. It is possible to overflow this buffer in such a way as to cause applications to fail, or possible system security violations.
fgets'in imzası şöyle
char * fgets ( char * str, int num, FILE * stream );
Metod şöyle kullanılır.

char name[256];
fgets(name,256,stdin);
Önemli bir nokta şu , gets newline karakterini de okunan string'e dahil eder!

fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer.
Bu karakterden kurtulmak için şöyle yapılabilir. Böylece \n karakteri yerine \0 koymuş oluruz.
while (fgets(buffer, sizeof buffer, stdin) != NULL) {
  size_t len = strlen(buffer);
  if (len > 0 && buffer[len - 1] == '\n')
    buffer[--len] = '\0';

  // use buffer
  printf("\"%s\"\n", buffer);
}
strlen yerine strcspn kullanılabilir. Veya strtok kullanılabilir. Her ne olursa olsun C ile dosya işlemleri ve string'ler yanyana gelmemesi gereken şeyler. İğrenç :)

ftruncate
Dosyayı verilen büyüklüğe getirir. Bu metod kullanılarak bir dosyanın başından ven sonundan bazı satırları silme örneği burada. ftruncate şu hataları dönebilir.
EROFS : File resides in a readonly file system.
EBADF : File is open for reading only
EINVAL : File descriptor refers to a file on which this operation is impossible.

Buffered I/O
C Dili
İşletim sistemi tarafından atanan 3 posix stream için C dilinde unistd.h dosyasında macrolar tanımlanmıştır. Bu macrolar aşağıdaki gibidir.

Ancak posix streamleri unbuffered I/O için tasarlanmışlardır. C dili ise buffered I/O yapmak için başka yapılar sunmaktadır. Bunlar yapılar stdio.h dosyasında tanımlıdır ve FILE yapılarıdır. Aşağıda bu durumu görebiliriz.



Yani C dilindeki stdin , stdout , stderr FILE struct'larıdır ve macrolardan farklı şeylerdir.

Burada bir konuya daha değinmek lazım. Nasıl olduğunu bilimiyorum ancak stderr stream'inin non-buffered olduğu yazıyor. Dolayısıyla stdout ve sterr aynı terminale yazdıklarında stdout çıktısı daha yazılmadan sterr çıktısını görebiliriz. Benzer sıkıntılar C# içinde geçerli.

Aşağıdaki şekli buradan aldım ve C dilinde buffered I/O yapıldığını daha iyi gösteriyor.
FILE struct'a Yazmak İçin
fprintf kullanılır. Burada dikkat edilmesi gereken nokta fprintf sadece FILE struct'a yazmaya yarar örneğin socket handle için kullanılamaz.

FILE Struct ile ilgili Diğer Metodlar
Listeyi buradan aldım.
fopen , fclose, freopen
fread, fwrite,
fflush,
fseek
fseek metodunu complexity'si burada tartışılmış.

fgetc,getc, getchar,
fputc, putc, putchar,
fputs

printf, fprintf, sprintf
scanf,fscanf,sscanf

fopen
Dosyayı sadece yazmak için açmak için "w" kullanmalıyız. Örnek:
FILE *file = fopen("/path","w")
freopen
freopen metodu yazısına taşıdım.

fputc
Tek bir karakter yazar.
fputc('1',file);
fputs
fputs sadece NULL ile biten string'i yazar. Eğer dosyaya NULL yazmak istiyorsak fwrite kullanmalıyız.

fscanf
scanf ailesinden olan bu metod ile dosyadan formatlı olarak veri okumak mümkün.


Buffer'ın Boyunu Değiştirmek
C Dili
I/O için kullanılan tampon'unun (buffer) boyunu değiştirmek için setbuf metodu kullanılır. Buffer yöntemini (unbuffered, line buffered, fully buffered) değiştirmek içinse  setvbuf metodu kullanılır.

setvbuf için örnek:
FILE* outf = //...
char obuffer[BUFSIZ];
//Full buffering: tampon tamamen dolunca yaz ve tamamen boşalınca oku
setvbuf(outf, obuffer, _IOFBF, sizeof(obuffer));
Buffer'ın boyunu seçerken BUFSIZ macrosu kullanılabilir. Bu macro her sistemde farklı bir değere sahip ve stream işlemleri için en uygun değeri taşıdığı yazılı.

NonBuffered I/O'dan Buffered I/O'ya Geçmek
C Dili
NonBuffered I/O'yu temsil eden bir stream fdopen() metodu ile buffered FILE* haline getirilebilir. Windows üzerinde ise _fdopen() kullanılır. Örnek:

int file;
FILE *stream = fdopen (file, "r");

Buffered I/O'dan NonBuffered I/O'ya Geçmek
C dili
Buffered I/O'yu temsil eden FILE* yapısı fileno (FILE* stream) metodu kullanılarak işletim sistemi tarafından kullanılan stream (file descriptor) numarasına çevirilebilir. Burada, gcc -std=gnu99 veya -D_POSIX_SOURCE seçenekleri ile derlenmesi gerektiği söylenmiş. Örnek:


Buffer'ı Boşaltmaya Zorlamak
C Dili
Bu iş için fflush() metodu kullanılıyor. Yanlız input için kullanılan bir stream üzerinde fflush kullanmak tanımsız bir davranış. Bu yüzden yapmamak lazım.

Buffer'ın Wide Oriented veya Byte Oriented Çalıştığını Kontrol Etmek
Sadece C dilinde  Byte-oriented veya Wide-oriented çalışma durumu var. Implementing perror() - issue sorusunda fwide kullanma örneği mevcut.
C++ Dili
std::cin , std::cout , std::cerr  C dilindeki yapılara karşılık geliyorlar. std::clog std::cerr ile aynı aycak std::cerr non-buffered iken std::clog buffered bir yapı.

C++ streamleri ile kullanılabilen özel karakterler var. 
\f : Ekranı temizlemek için kullanılabilir
\t : Bir tab karakteri koymak için kullanılabilir.

Java Dili
Java'da Flushable arayüzü ve bu arayüzü gerçekleştiren bir sürü sınıf var.

Bir diğer ilginç nokta ise Java ile gelen InputStream sınıfının non-buffered olmasına rağmen yine de available() diye bir metod sunması. Bu metod ile bloke olmadan okunabilecek byte sayısını almak mümkün

22 Nisan 2015 Çarşamba

Thread'i Durdurmak

Giriş
Thread'i kolay ve temiz bir şekilde durdurmak her programlama dilinde sıkıntıdır. Aşağıda notlarım var.

Worker Thread
Bu tür thread'leri durdurmanın en temiz yolu, kuyruğa özel bir job eklemek. Örnek
void SimpleWorkQueue::tellAllThreadsToStop()
{
    std::unique_lock<std::mutex> locker(lock);
    stopping = true;

    for(std::thread& thread: workers)
    {
        work.push_back([](){ throw TerminateThread(); });
    }
}


// Now our main thread loop.
// looks like this.
// Note: We can remove 'finished' from everywhere.
void SimpleWorkQueue::workerAction()
{
    while(true)
    {
        std::function<void()> item = getNextWorkItem();
        try
        {
            item();
        }
        catch(TerminateThread const& e)
        {
            break;
        }
        catch(...)
        {
        }
    }
}

WIN32
TerminateThread() asla kullanılmamalı. Win32 ile bir thread sınıfı gelmediği için durdurma kontrolünü kendimiz yazmalıyız. Thread'i başlatmadan önce bir event yaratırız. Event'ler aynı bir bayrak gibi kullanılabilir.
HANDLE hevent_killme = CreateEvent(...);
Daha sonra bu event thread'e geçilir.

static DWORD WINAPI thread_callback (LPVOID param)
{
  ...
  while(WaitForSingleObject(hevent_killme, 0) != WAIT_OBJECT_0)
  {
    // do stuff
  }

  return 0;
}
Thread'i durdurmak isteyen event'i tetikler.
void killthread (void)
{
  SetEvent(hevent_killme);
  WaitForSingleObject(hthread_the_thread, INFINITE);
  CloseHandle(hevent_killme);
  CloseHandle(hthread_the_thread);
} 

Java
Thread.stop
Bu metod depricate edilmiş ve kullanılmamalı. Sebebi şu:

This method is inherently unsafe. Stopping a thread with Thread.stop causes it to unlock all of the monitors that it has locked (as a natural consequence of the unchecked ThreadDeath exception propagating up the stack). If any of the objects previously protected by these monitors were in an inconsistent state, the damaged objects become visible to other threads, potentially resulting in arbitrary behavior.

Thread.Interrupt
Bir thread'i durdumanın en kolay ve temiz yolu Thread.Interrupt () metodunu çağırmak.

An interrupt is an indication to a thread that it should stop what it is doing and do something else. It's up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate.


Thread.Interrupt Ne Yapar?
Metod aslında sadece bir bayrağı kaldırır. Metod tamamen işbirliği esasına dayanır. Bir thread asla bir başka thread'i durdurmaya zorlayamaz. Diğer thread'in de gönül rızası göstererek durmayı kabul etmesi gerekir.


Interrupts this thread. First the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown.

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

If this thread is blocked in an I/O operation upon an interruptible channel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a ClosedByInterruptException.

If this thread is blocked in a Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector's wakeup method were invoked.

If none of the previous conditions hold then this thread's interrupt status will be set.

Thread.interrupted
Çalışan thread döngü içinde interrupted() metodunu ile durdurulmasının talep edilip edilmediğini kontrol etmeli. Metodun açıklaması şöyle. Thread bu metodu çağırdıktan sonra bayrak indirilir.

Eğer thread kendi içinde halen bu bayrağı kontrol ediyorsa, yine thread içinde Thread.currentThread.interrupt() ile bayrağı tekrar kaldırmak gerekebilir.

static boolean interrupted(): Tests whether the current thread has been interrupted.
Basit bir örnek
public void run()
{
    while(!Thread.interrupted)
    {
     ...
    }
}

Thread.isInterrupted
Eğer başka bir thread'in interrupt edilip edilmediğini anlamak istersek isInterrupted() metodu kullanılır

boolean isInterrupted(): Tests whether this thread has been interrupted.

InterruptedException
Eğer interrupt edilen thread bir nesne üzerinde bloke olmuş bekliyorsa InterruptedException alır.

Thread.isInterrupted true dönerse InterruptedException atılması öneriliyor. Ancak interrupt edildiğimizi anladıktan sonra neden exception atmak lazım anlamadım.
if (Thread.interrupted()) throw new InterruptedException();